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