diff --git a/internal/scanner/scanner.go b/internal/scanner/scanner.go index 7755c639..0333549a 100644 --- a/internal/scanner/scanner.go +++ b/internal/scanner/scanner.go @@ -9,8 +9,6 @@ import ( "math/rand" "net/http" "net/url" - "os" - "os/signal" "regexp" "strings" "sync" @@ -337,32 +335,11 @@ func (s *Scanner) Run(ctx context.Context) error { // separately. var requestsCounter uint64 - userSignal := make(chan os.Signal, 1) - signal.Notify(userSignal, syscall.SIGUSR1) - defer func() { - signal.Stop(userSignal) - close(userSignal) - }() - - go func() { - for { - select { - case _, ok := <-userSignal: - if !ok { - return - } - - s.logger. - WithFields(logrus.Fields{ - "sent": atomic.LoadUint64(&requestsCounter), - "total": s.db.NumberOfTests, - }).Info("Testing status") - - case <-ctx.Done(): - return - } - } - }() + closeListener, err := s.listeningForPrintTestStatus(ctx, &requestsCounter) + if err != nil { + return err + } + defer closeListener() for e := 0; e < gn; e++ { go func() { diff --git a/internal/scanner/test_status_linux.go b/internal/scanner/test_status_linux.go new file mode 100644 index 00000000..276323ce --- /dev/null +++ b/internal/scanner/test_status_linux.go @@ -0,0 +1,40 @@ +package scanner + +import ( + "context" + "os" + "os/signal" + "sync/atomic" + "syscall" + + "github.com/sirupsen/logrus" +) + +func (s *Scanner) listeningForPrintTestStatus(ctx context.Context, requestsCounter *uint64) (func(), error) { + userSignal := make(chan os.Signal, 1) + signal.Notify(userSignal, syscall.SIGUSR1) + + go func() { + for { + select { + case _, ok := <-userSignal: + if !ok { + return + } + + s.logger. + WithFields(logrus.Fields{ + "sent": atomic.LoadUint64(requestsCounter), + "total": s.db.NumberOfTests, + }).Info("Testing status") + + case <-ctx.Done(): + return + } + } + }() + return func() { + signal.Stop(userSignal) + close(userSignal) + }, nil +} diff --git a/internal/scanner/test_status_windows.go b/internal/scanner/test_status_windows.go new file mode 100644 index 00000000..712b93ac --- /dev/null +++ b/internal/scanner/test_status_windows.go @@ -0,0 +1,94 @@ +package scanner + +import ( + "context" + "sync/atomic" + "unsafe" + + "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" +) + +type k32Event struct { + keyDown int32 + repeatCount uint16 + virtualKeyCode uint16 + virtualScanCode uint16 + unicodeChar uint16 + controlKeyState uint32 +} + +var ( + kernel32 = windows.NewLazyDLL("kernel32.dll") + k32_ReadConsoleInputW = kernel32.NewProc("ReadConsoleInputW") +) + +func keyboardListener(ctx context.Context, c chan struct{}) error { + hInterrupt, err := windows.CreateEvent(nil, 0, 0, nil) + if err != nil { + return err + } + + hConsoleIn, err := windows.Open("CONIN$", windows.O_RDWR, 0) + if err != nil { + windows.Close(hInterrupt) + return err + } + go func() { + for { + select { + case <-ctx.Done(): + return + default: + _, err := windows.WaitForSingleObject(hConsoleIn, uint32(windows.INFINITE)) + if err != nil { + continue + } + + var input [20]uint16 + var numberOfEventsRead uint32 + r0, _, err := k32_ReadConsoleInputW.Call(uintptr(hConsoleIn), uintptr(unsafe.Pointer(&input[0])), 1, uintptr(unsafe.Pointer(&numberOfEventsRead))) + if int(r0) == 0 { + continue + } + if input[0] == 0x1 { + kEvent := (*k32Event)(unsafe.Pointer(&input[2])) + keyCode := kEvent.virtualKeyCode + ctrlPressed := kEvent.controlKeyState&(0x08|0x04) != 0 + + // check if Ctrl + B pressed + if ctrlPressed && keyCode == 0x42 { + c <- struct{}{} + } + } + } + } + }() + return nil +} + +// On windows, it listens for the Ctrl + B keypress +func (s *Scanner) listeningForPrintTestStatus(ctx context.Context, requestsCounter *uint64) (func(), error) { + userSignal := make(chan struct{}, 1) + if err := keyboardListener(ctx, userSignal); err != nil { + return nil, err + } + s.logger.Info("Press Ctrl + B view testing status") + go func() { + for { + select { + case <-userSignal: + s.logger. + WithFields(logrus.Fields{ + "sent": atomic.LoadUint64(requestsCounter), + "total": s.db.NumberOfTests, + }).Info("Testing status") + case <-ctx.Done(): + return + } + } + }() + return func() { + close(userSignal) + }, nil +}