github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/snapstate/backend/locking.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 package backend 20 21 import ( 22 "github.com/snapcore/snapd/cmd/snaplock" 23 "github.com/snapcore/snapd/cmd/snaplock/runinhibit" 24 "github.com/snapcore/snapd/osutil" 25 "github.com/snapcore/snapd/snap" 26 ) 27 28 func (b Backend) RunInhibitSnapForUnlink(info *snap.Info, hint runinhibit.Hint, decision func() error) (lock *osutil.FileLock, err error) { 29 // A process may be created after the soft refresh done upon 30 // the request to refresh a snap. If such process is alive by 31 // the time this code is reached the refresh process is stopped. 32 33 // Grab per-snap lock to prevent new processes from starting. This is 34 // sufficient to perform the check, even though individual processes 35 // may fork or exit, we will have per-security-tag information about 36 // what is running. 37 lock, err = snaplock.OpenLock(info.InstanceName()) 38 if err != nil { 39 return nil, err 40 } 41 // Keep a copy of lock, so that we can close it in the function below. 42 // The regular lock variable is assigned to by return, due to the named 43 // return values. 44 lockToClose := lock 45 defer func() { 46 // If we have a lock but we are returning an error then unlock the lock 47 // by closing it. 48 if lockToClose != nil && err != nil { 49 lockToClose.Close() 50 } 51 }() 52 if err := lock.Lock(); err != nil { 53 return nil, err 54 } 55 // 56 if err := decision(); err != nil { 57 return nil, err 58 } 59 // Decision function did not fail so we can, while we still hold the snap 60 // lock, install the snap run inhibition hint, returning the snap lock to 61 // the caller. 62 // 63 // XXX: should we move this logic to the place that calls the "soft" 64 // check instead? Doing so would somewhat change the semantic of soft 65 // and hard checks, as it would effectively make hard check a no-op, 66 // but it might provide a nicer user experience. 67 if err := runinhibit.LockWithHint(info.InstanceName(), hint); err != nil { 68 return nil, err 69 } 70 return lock, nil 71 } 72 73 // WithSnapLock executes given action with the snap lock held. 74 // 75 // The lock is also used by snap-confine during pre-snap mount namespace 76 // initialization. Holding it allows to ensure mutual exclusion during the 77 // process of preparing a new snap app or hook processes. It does not prevent 78 // existing application or hook processes from forking. 79 // 80 // Note that this is not a method of the Backend type, so that it can be 81 // invoked from doInstall, which does not have access to a backend object. 82 func WithSnapLock(info *snap.Info, action func() error) error { 83 lock, err := snaplock.OpenLock(info.InstanceName()) 84 if err != nil { 85 return err 86 } 87 // Closing the lock also unlocks it, if locked. 88 defer lock.Close() 89 if err := lock.Lock(); err != nil { 90 return err 91 } 92 return action() 93 }