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 (
|
||||
"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
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