forked from snapcore/snapd
/
inhibit.go
154 lines (135 loc) · 4.17 KB
/
inhibit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2020 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Package runinhibit contains operations for establishing, removing and
// querying snap run inhibition lock.
package runinhibit
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
)
// defaultInhibitDir is the directory where inhibition files are stored.
const defaultInhibitDir = "/var/lib/snapd/inhibit"
// InhibitDir is the directory where inhibition files are stored.
// This value can be changed by calling dirs.SetRootDir.
var InhibitDir = defaultInhibitDir
func init() {
dirs.AddRootDirCallback(func(root string) {
InhibitDir = filepath.Join(root, defaultInhibitDir)
})
}
// Hint is a string representing reason for the inhibition of "snap run".
type Hint string
const (
// HintNotInhibited is used when "snap run" is not inhibited.
HintNotInhibited Hint = ""
// HintInhibitedForRefresh represents inhibition of a "snap run" while a refresh change is being performed.
HintInhibitedForRefresh Hint = "refresh"
)
func hintFile(snapName string) string {
return filepath.Join(InhibitDir, snapName+".lock")
}
func openHintFileLock(snapName string) (*osutil.FileLock, error) {
return osutil.NewFileLockWithMode(hintFile(snapName), 0644)
}
// LockWithHint sets a persistent "snap run" inhibition lock, for the given snap, with a given hint.
//
// The hint cannot be empty. It should be one of the Hint constants defined in
// this package. With the hint in place "snap run" will not allow the snap to
// start and will block, presenting a user interface if possible.
func LockWithHint(snapName string, hint Hint) error {
if len(hint) == 0 {
return fmt.Errorf("lock hint cannot be empty")
}
if err := os.MkdirAll(InhibitDir, 0755); err != nil {
return err
}
flock, err := openHintFileLock(snapName)
if err != nil {
return err
}
defer flock.Close()
if err := flock.Lock(); err != nil {
return err
}
f := flock.File()
if err := f.Truncate(0); err != nil {
return err
}
_, err = f.WriteString(string(hint))
return err
}
// Unlock truncates the run inhibition lock, for the given snap.
//
// An empty inhibition lock means uninhibited "snap run".
func Unlock(snapName string) error {
flock, err := openHintFileLock(snapName)
if os.IsNotExist(err) {
return nil
}
if err != nil {
return err
}
defer flock.Close()
if err := flock.Lock(); err != nil {
return err
}
f := flock.File()
return f.Truncate(0)
}
// IsLocked returns the state of the run inhibition lock for the given snap.
//
// It returns the current, non-empty hit if inhibition is in place. Otherwise
// it returns an empty hint.
func IsLocked(snapName string) (Hint, error) {
fname := filepath.Join(InhibitDir, snapName+".lock")
flock, err := osutil.OpenExistingLockForReading(fname)
if os.IsNotExist(err) {
return "", nil
}
if err != nil {
return "", err
}
defer flock.Close()
if err := flock.ReadLock(); err != nil {
return "", err
}
buf, err := ioutil.ReadAll(flock.File())
if err != nil {
return "", err
}
return Hint(string(buf)), nil
}
// RemoveLockFile removes the run inhibition lock for the given snap.
//
// This function should not be used as a substitute of Unlock, as race-free
// ability to inspect the inhibition state relies on flock(2) which requires the
// file to exist in the first place and non-privileged processes cannot create
// it.
//
// The function does not fail if the inhibition lock does not exist.
func RemoveLockFile(snapName string) error {
err := os.Remove(hintFile(snapName))
if err != nil && !os.IsNotExist(err) {
return err
}
return nil
}