53 lines
1.2 KiB
Go
53 lines
1.2 KiB
Go
package processlock
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
var ErrLocked = errors.New("进程锁已被占用")
|
|
|
|
type Lock struct {
|
|
file *os.File
|
|
}
|
|
|
|
func Acquire(path string) (*Lock, error) {
|
|
path = filepath.Clean(path)
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
|
return nil, fmt.Errorf("创建进程锁目录失败:%w", err)
|
|
}
|
|
if info, err := os.Lstat(path); err == nil {
|
|
if info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular() {
|
|
return nil, errors.New("进程锁路径必须是普通文件")
|
|
}
|
|
} else if !errors.Is(err, os.ErrNotExist) {
|
|
return nil, fmt.Errorf("检查进程锁失败:%w", err)
|
|
}
|
|
|
|
file, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o600)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("打开进程锁失败:%w", err)
|
|
}
|
|
if err := lockFile(file); err != nil {
|
|
_ = file.Close()
|
|
return nil, err
|
|
}
|
|
if err := os.Chmod(path, 0o600); err != nil {
|
|
_ = unlockFile(file)
|
|
_ = file.Close()
|
|
return nil, fmt.Errorf("设置进程锁权限失败:%w", err)
|
|
}
|
|
return &Lock{file: file}, nil
|
|
}
|
|
|
|
func (lock *Lock) Close() error {
|
|
if lock == nil || lock.file == nil {
|
|
return nil
|
|
}
|
|
file := lock.file
|
|
lock.file = nil
|
|
return errors.Join(unlockFile(file), file.Close())
|
|
}
|