Ensure only a single instance of after-lock runs at a time

This commit is contained in:
Kristóf Tóth 2020-08-20 15:57:17 +02:00
parent 8b89bcb3f6
commit e6458aba0b
3 changed files with 151 additions and 0 deletions

View File

@ -2,19 +2,50 @@ package main
import (
"after-lock/afterlock"
"after-lock/lockfile"
"fmt"
"runtime/debug"
"os"
)
const (
initialDelay = 3
loopDelay = 6
keyboardDeviceNode = "/dev/input/by-id/usb-Dell_Dell_USB_Entry_Keyboard-event-kbd"
lockfilePath = "/tmp/after-lock.lock"
printStackTraces = false
)
func main() {
defer handleErrors()
lock := grabExclusiveProcessLock()
defer lock.Unlock()
al := afterlock.New(keyboardDeviceNode)
al.InitialDelay = initialDelay
al.LoopDelay = loopDelay
al.Start()
}
func handleErrors() {
if err := recover(); err != nil {
fmt.Printf("Crashed with unexpected error: %v!\n", err)
if printStackTraces {
fmt.Printf("Stack trace:\n\n")
debug.PrintStack()
}
os.Exit(1)
}
}
func grabExclusiveProcessLock() *lockfile.Lockfile {
lf := lockfile.New(lockfilePath)
err := lf.Lock()
if err != nil {
panic(err)
}
return lf
}

49
lockfile/lockfile.go Normal file
View File

@ -0,0 +1,49 @@
package lockfile
import (
"errors"
"os"
"syscall"
)
type Lockfile struct {
path string
fd int
}
func New(path string) *Lockfile {
return &Lockfile{path: path}
}
func (lf *Lockfile) Lock() error {
fd, err := syscall.Open(lf.path, syscall.O_CREAT | syscall.O_RDWR, 0600)
if err != nil {
return err
}
lf.fd = fd
err = syscall.Flock(lf.fd, syscall.LOCK_EX | syscall.LOCK_NB)
if err != nil {
return err
}
return nil
}
func (lf *Lockfile) Unlock() error {
if lf.fd == 0 {
panic(errors.New("Lockfile was not locked"))
}
err := syscall.Flock(lf.fd, syscall.LOCK_UN)
if err != nil {
return err
}
err = syscall.Close(lf.fd)
if err != nil {
return err
}
os.Remove(lf.path)
if err != nil {
return err
}
return nil
}

71
lockfile/lockfile_test.go Normal file
View File

@ -0,0 +1,71 @@
package lockfile_test
import (
"io/ioutil"
"os"
"testing"
"after-lock/lockfile"
)
func tmpFile() string {
tf, err := ioutil.TempFile("", "lockfile_test*")
if err != nil {
panic(err)
}
return tf.Name()
}
func TestNotLockedUnlockPanics(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Errorf("Unlock() did not panic")
}
}()
tf := tmpFile()
defer os.Remove(tf)
err := lockfile.New(tf).Unlock()
if err != nil {
panic(err)
}
}
func TestSecondLockFails(t *testing.T) {
tf := tmpFile()
defer os.Remove(tf)
fl1 := lockfile.New(tf)
err := fl1.Lock()
if err != nil {
panic(err)
}
defer fl1.Unlock()
fl2 := lockfile.New(tf)
err = fl2.Lock()
if err == nil {
t.Errorf("Second Lock() did not fail")
}
}
func TestLockUnlock(t *testing.T) {
tf := tmpFile()
defer os.Remove(tf)
fl := lockfile.New(tf)
err := fl.Lock()
if err != nil {
panic(err)
}
err = fl.Unlock()
if err != nil {
panic(err)
}
if _, err := os.Stat(tf); !os.IsNotExist(err) {
t.Errorf("Lockfile %s still exists", tf)
}
}