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()) }