diff --git a/cmd/root.go b/cmd/root.go index e9f4a7d..dcb8565 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -6,6 +6,8 @@ import ( "os" "strings" + "github.com/dominicbreuker/pspy/internal/config" + "github.com/dominicbreuker/pspy/internal/logger" "github.com/dominicbreuker/pspy/internal/pspy" "github.com/spf13/cobra" ) @@ -63,7 +65,15 @@ func root(cmd *cobra.Command, args []string) { fmt.Printf("Watching recursively : %+v (%d)\n", rDirs, len(rDirs)) fmt.Printf("Watching non-recursively: %+v (%d)\n", dirs, len(dirs)) fmt.Printf("Printing: processes=%t file-system events=%t\n", logPS, logFS) - pspy.Watch(rDirs, dirs, logPS, logFS) + cfg := config.Config{ + RDirs: rDirs, + Dirs: dirs, + LogPS: logPS, + LogFS: logFS, + } + logger := logger.NewLogger() + pspy.Start(cfg, logger) + // pspy.Watch(rDirs, dirs, logPS, logFS) } func Execute() { diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..287a97a --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,14 @@ +package config + +import "fmt" + +type Config struct { + RDirs []string + Dirs []string + LogFS bool + LogPS bool +} + +func (c Config) String() string { + return fmt.Sprintf("Printing events: processes: %t | file system events: %t | Watching directories: %+v (recursive) | %+v (non-recursive)", c.LogPS, c.LogFS, c.RDirs, c.Dirs) +} diff --git a/internal/logger/logger.go b/internal/logger/logger.go new file mode 100644 index 0000000..7e4aca9 --- /dev/null +++ b/internal/logger/logger.go @@ -0,0 +1,37 @@ +package logger + +import ( + "log" + "os" +) + +// Logger is the logger used to print to the command line +type Logger struct { + infoLogger *log.Logger + errorLogger *log.Logger + eventLogger *log.Logger +} + +// NewLogger creates a new logger instance +func NewLogger() *Logger { + return &Logger{ + infoLogger: log.New(os.Stdout, "", 0), + errorLogger: log.New(os.Stderr, "", 0), + eventLogger: log.New(os.Stdout, "", log.Ldate|log.Ltime), + } +} + +// Infof writes an info message to stdout +func (l *Logger) Infof(format string, v ...interface{}) { + l.infoLogger.Printf(format, v...) +} + +// Errorf writes an error message to stderr +func (l *Logger) Errorf(format string, v ...interface{}) { + l.errorLogger.Printf(format, v...) +} + +// Eventf writes an event with timestamp to stdout +func (l *Logger) Eventf(format string, v ...interface{}) { + l.eventLogger.Printf(format, v...) +} diff --git a/internal/logger/logger_test.go b/internal/logger/logger_test.go new file mode 100644 index 0000000..8d19451 --- /dev/null +++ b/internal/logger/logger_test.go @@ -0,0 +1,41 @@ +package logger + +import ( + "bytes" + "log" + "regexp" + "testing" +) + +const dateFormatPattern = `[\d]{4}/[\d]{2}/[\d]{2} [\d]{2}:[\d]{2}:[\d]{2}` + +var l = NewLogger() + +var logTests = []struct { + logger *log.Logger + test func() + expectation string +}{ + {l.infoLogger, func() { l.Infof("Info message no. %d", 1) }, "Info message no. 1\n"}, + {l.infoLogger, func() { l.Infof("Info message no. %d with a string %s\n", 2, "appended to it") }, "Info message no. 2 with a string appended to it\n"}, + {l.errorLogger, func() { l.Errorf("Error message") }, "Error message\n"}, + {l.errorLogger, func() { l.Errorf("Error message\n") }, "Error message\n"}, + {l.eventLogger, func() { l.Eventf("Event message") }, dateFormatPattern + " Event message\n"}, +} + +func TestLogging(t *testing.T) { + for i, tt := range logTests { + actual := captureOutput(tt.logger, tt.test) + matcher := regexp.MustCompile(tt.expectation) + if !matcher.Match([]byte(actual)) { + t.Fatalf("[%d] Wrong message logged!: %s", i, actual) + } + } +} + +func captureOutput(logger *log.Logger, f func()) string { + var buf bytes.Buffer + logger.SetOutput(&buf) + f() + return buf.String() +} diff --git a/internal/pspy/pspy.go b/internal/pspy/pspy.go index 207b030..54d9df9 100644 --- a/internal/pspy/pspy.go +++ b/internal/pspy/pspy.go @@ -4,19 +4,62 @@ import ( "fmt" "log" "os" + "os/signal" + "syscall" "time" + "github.com/dominicbreuker/pspy/internal/config" "github.com/dominicbreuker/pspy/internal/inotify" + "github.com/dominicbreuker/pspy/internal/logger" "github.com/dominicbreuker/pspy/internal/process" "github.com/dominicbreuker/pspy/internal/walker" ) -const MaxInt = int(^uint(0) >> 1) +func Start(cfg config.Config, logger *logger.Logger) { + fmt.Printf("Config: %+v\n", cfg) -func Monitor() { - Watch([]string{"/tmp"}, nil, true, true) + triggerCh, fsEventCh, err := setupInotify(cfg.RDirs, cfg.Dirs) + if err != nil { + logger.Errorf("Can't set up inotify watchers: %v\n", err) + } + psEventCh, err := setupProcfsScanner(triggerCh, 100*time.Millisecond) + if err != nil { + logger.Errorf("Can't set up procfs scanner: %+v\n", err) + } + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + + for { + select { + case se := <-sigCh: + logger.Infof("Exiting program... (%s)\n", se) + os.Exit(0) + case fe := <-fsEventCh: + if cfg.LogFS { + logger.Eventf("FS: %+v\n", fe) + } + case pe := <-psEventCh: + if cfg.LogPS { + logger.Eventf("CMD: %+v\n", pe) + } + } + } } +func setupInotify(rdirs, dirs []string) (chan struct{}, chan string, error) { + triggerCh := make(chan struct{}) + fsEventCh := make(chan string) + return triggerCh, fsEventCh, nil +} + +func setupProcfsScanner(triggerCh chan struct{}, interval time.Duration) (chan string, error) { + psEventCh := make(chan string) + return psEventCh, nil +} + +const MaxInt = int(^uint(0) >> 1) + func Watch(rdirs, dirs []string, logPS, logFS bool) { maxWatchers, err := inotify.WatcherLimit() if err != nil {