-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fsnotify-backed recursive watcher for Linux (#5833)
Adds a wrapper on top of fsnotify that allows to place recursive watches on directories. Exhibits similar behavior to FSEvents. This adds an explanation to the `file.recursive` flag and also updates the description of the underlying implementation for different OSes, to account for recent changes. * Document file.recursive flag. * fsnotify-backed recursive watcher * Update CHANGELOG for recursive file monitoring * Updated auditbeat file metric docs
- Loading branch information
Showing
11 changed files
with
806 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package monitor | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
path_pkg "path" | ||
"strings" | ||
) | ||
|
||
// VisitOrder is a two-valued flag used to control how directories are visited. | ||
type VisitOrder int8 | ||
|
||
const ( | ||
// PreOrder has directories visited before their contents. | ||
PreOrder VisitOrder = iota | ||
// PostOrder has directories visited after their contents. | ||
PostOrder | ||
) | ||
|
||
var ( | ||
// PathSeparator can be used to override the operating system separator. | ||
PathSeparator = string(os.PathSeparator) | ||
) | ||
|
||
// FileTree represents a directory in a filesystem-tree structure. | ||
type FileTree map[string]FileTree | ||
|
||
// VisitFunc is the type for a callback to visit the entries on a directory | ||
// and its subdirectories. | ||
type VisitFunc func(path string, isDir bool) error | ||
|
||
// AddFile adds a file to a FileTree. If the path includes subdirectories | ||
// they are created as necessary. | ||
func (tree FileTree) AddFile(path string) error { | ||
return tree.add(path_pkg.Clean(path), nil) | ||
} | ||
|
||
// AddDir adds a directory to a FileTree. If the path includes subdirectories | ||
// they are created as necessary. | ||
func (tree FileTree) AddDir(path string) error { | ||
return tree.add(path_pkg.Clean(path), FileTree{}) | ||
} | ||
|
||
// Remove an entry from a FileTree. | ||
func (tree FileTree) Remove(path string) error { | ||
components := strings.Split(path, PathSeparator) | ||
last := -1 | ||
for pos := len(components) - 1; pos >= 0; pos-- { | ||
if len(components[pos]) != 0 { | ||
last = pos | ||
break | ||
} | ||
} | ||
if last > 0 { | ||
subtree, err := tree.getByComponents(path, components[:last]) | ||
if err != nil { | ||
return err | ||
} | ||
delete(subtree, components[last]) | ||
} | ||
return nil | ||
} | ||
|
||
// Visit calls the callback function for the given path and recursively all its | ||
// contents, if a directory path is passed. | ||
func (tree FileTree) Visit(path string, order VisitOrder, fn VisitFunc) error { | ||
entry, err := tree.At(path) | ||
if err != nil { | ||
return err | ||
} | ||
return entry.visitDirRecursive(path_pkg.Clean(path), order, fn) | ||
} | ||
|
||
// At returns a new FileTree rooted at the given path. | ||
func (tree FileTree) At(path string) (FileTree, error) { | ||
return tree.getByComponents(path, strings.Split(path, PathSeparator)) | ||
} | ||
|
||
func (tree FileTree) add(path string, value FileTree) error { | ||
components := strings.Split(path, PathSeparator) | ||
dir, last := tree, len(components)-1 | ||
for i := 0; i < last; i++ { | ||
if len(components[i]) == 0 { | ||
continue | ||
} | ||
if next, exists := dir[components[i]]; exists { | ||
if next == nil { | ||
return fmt.Errorf("directory expected: '%s' in %s", components[i], path) | ||
} | ||
dir = next | ||
} else { | ||
newDir := FileTree{} | ||
dir[components[i]] = newDir | ||
dir = newDir | ||
} | ||
} | ||
dir[components[last]] = value | ||
return nil | ||
} | ||
|
||
func (tree FileTree) getByComponents(path string, components []string) (FileTree, error) { | ||
dir, exists := tree, false | ||
for _, item := range components { | ||
if len(item) != 0 { | ||
if dir == nil { | ||
// previous component is a file, not a directory | ||
return nil, fmt.Errorf("path component %s is a file: %s", item, path) | ||
} | ||
if dir, exists = dir[item]; !exists { | ||
return nil, fmt.Errorf("path component %s not found in %s", item, path) | ||
} | ||
} | ||
} | ||
return dir, nil | ||
} | ||
|
||
func (tree FileTree) visitDirRecursive(path string, order VisitOrder, fn VisitFunc) error { | ||
if tree == nil { | ||
return fn(path, false) | ||
} | ||
if order == PreOrder { | ||
if err := fn(path, true); err != nil { | ||
return err | ||
} | ||
} | ||
for name, content := range tree { | ||
fullpath := path_pkg.Join(path, name) | ||
if content == nil { | ||
if err := fn(fullpath, false); err != nil { | ||
return err | ||
} | ||
} else { | ||
if err := content.visitDirRecursive(fullpath, order, fn); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
if order == PostOrder { | ||
return fn(path, true) | ||
} | ||
return nil | ||
} |
Oops, something went wrong.