Ensure only a single instance of after-lock runs at a time
This commit is contained in:
parent
8b89bcb3f6
commit
e6458aba0b
@ -2,19 +2,50 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"after-lock/afterlock"
|
"after-lock/afterlock"
|
||||||
|
"after-lock/lockfile"
|
||||||
|
"fmt"
|
||||||
|
"runtime/debug"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
initialDelay = 3
|
initialDelay = 3
|
||||||
loopDelay = 6
|
loopDelay = 6
|
||||||
keyboardDeviceNode = "/dev/input/by-id/usb-Dell_Dell_USB_Entry_Keyboard-event-kbd"
|
keyboardDeviceNode = "/dev/input/by-id/usb-Dell_Dell_USB_Entry_Keyboard-event-kbd"
|
||||||
|
lockfilePath = "/tmp/after-lock.lock"
|
||||||
|
printStackTraces = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
defer handleErrors()
|
||||||
|
|
||||||
|
lock := grabExclusiveProcessLock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
|
||||||
al := afterlock.New(keyboardDeviceNode)
|
al := afterlock.New(keyboardDeviceNode)
|
||||||
al.InitialDelay = initialDelay
|
al.InitialDelay = initialDelay
|
||||||
al.LoopDelay = loopDelay
|
al.LoopDelay = loopDelay
|
||||||
|
|
||||||
al.Start()
|
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
49
lockfile/lockfile.go
Normal 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
71
lockfile/lockfile_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user