github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/daemon/snap.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-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 daemon 21 22 import ( 23 "errors" 24 "fmt" 25 "os" 26 "path/filepath" 27 28 "github.com/snapcore/snapd/client" 29 "github.com/snapcore/snapd/client/clientutil" 30 "github.com/snapcore/snapd/logger" 31 "github.com/snapcore/snapd/overlord/assertstate" 32 "github.com/snapcore/snapd/overlord/healthstate" 33 "github.com/snapcore/snapd/overlord/snapstate" 34 "github.com/snapcore/snapd/overlord/state" 35 "github.com/snapcore/snapd/snap" 36 ) 37 38 var errNoSnap = errors.New("snap not installed") 39 40 type aboutSnap struct { 41 info *snap.Info 42 snapst *snapstate.SnapState 43 health *client.SnapHealth 44 } 45 46 // localSnapInfo returns the information about the current snap for the given name plus the SnapState with the active flag and other snap revisions. 47 func localSnapInfo(st *state.State, name string) (aboutSnap, error) { 48 st.Lock() 49 defer st.Unlock() 50 51 var snapst snapstate.SnapState 52 err := snapstate.Get(st, name, &snapst) 53 if err != nil && err != state.ErrNoState { 54 return aboutSnap{}, fmt.Errorf("cannot consult state: %v", err) 55 } 56 57 info, err := snapst.CurrentInfo() 58 if err == snapstate.ErrNoCurrent { 59 return aboutSnap{}, errNoSnap 60 } 61 if err != nil { 62 return aboutSnap{}, fmt.Errorf("cannot read snap details: %v", err) 63 } 64 65 info.Publisher, err = publisherAccount(st, info.SnapID) 66 if err != nil { 67 return aboutSnap{}, err 68 } 69 70 health, err := healthstate.Get(st, name) 71 if err != nil { 72 return aboutSnap{}, err 73 } 74 75 return aboutSnap{ 76 info: info, 77 snapst: &snapst, 78 health: clientHealthFromHealthstate(health), 79 }, nil 80 } 81 82 // allLocalSnapInfos returns the information about the all current snaps and their SnapStates. 83 func allLocalSnapInfos(st *state.State, all bool, wanted map[string]bool) ([]aboutSnap, error) { 84 st.Lock() 85 defer st.Unlock() 86 87 snapStates, err := snapstate.All(st) 88 if err != nil { 89 return nil, err 90 } 91 about := make([]aboutSnap, 0, len(snapStates)) 92 93 healths, err := healthstate.All(st) 94 if err != nil { 95 return nil, err 96 } 97 98 var firstErr error 99 for name, snapst := range snapStates { 100 if len(wanted) > 0 && !wanted[name] { 101 continue 102 } 103 health := clientHealthFromHealthstate(healths[name]) 104 var aboutThis []aboutSnap 105 var info *snap.Info 106 var err error 107 if all { 108 for _, seq := range snapst.Sequence { 109 info, err = snap.ReadInfo(name, seq) 110 if err != nil { 111 // single revision may be broken 112 _, instanceKey := snap.SplitInstanceName(name) 113 info = &snap.Info{ 114 SideInfo: *seq, 115 InstanceKey: instanceKey, 116 Broken: err.Error(), 117 } 118 // clear the error 119 err = nil 120 } 121 info.Publisher, err = publisherAccount(st, seq.SnapID) 122 if err != nil && firstErr == nil { 123 firstErr = err 124 } 125 aboutThis = append(aboutThis, aboutSnap{info, snapst, health}) 126 } 127 } else { 128 info, err = snapst.CurrentInfo() 129 if err == nil { 130 info.Publisher, err = publisherAccount(st, info.SnapID) 131 aboutThis = append(aboutThis, aboutSnap{info, snapst, health}) 132 } 133 } 134 135 if err != nil { 136 // XXX: aggregate instead? 137 if firstErr == nil { 138 firstErr = err 139 } 140 continue 141 } 142 about = append(about, aboutThis...) 143 } 144 145 return about, firstErr 146 } 147 148 func publisherAccount(st *state.State, snapID string) (snap.StoreAccount, error) { 149 if snapID == "" { 150 return snap.StoreAccount{}, nil 151 } 152 153 pubAcct, err := assertstate.Publisher(st, snapID) 154 if err != nil { 155 return snap.StoreAccount{}, fmt.Errorf("cannot find publisher details: %v", err) 156 } 157 return snap.StoreAccount{ 158 ID: pubAcct.AccountID(), 159 Username: pubAcct.Username(), 160 DisplayName: pubAcct.DisplayName(), 161 Validation: pubAcct.Validation(), 162 }, nil 163 } 164 165 func clientHealthFromHealthstate(h *healthstate.HealthState) *client.SnapHealth { 166 if h == nil { 167 return nil 168 } 169 return &client.SnapHealth{ 170 Revision: h.Revision, 171 Timestamp: h.Timestamp, 172 Status: h.Status.String(), 173 Message: h.Message, 174 Code: h.Code, 175 } 176 } 177 178 func mapLocal(about aboutSnap, sd clientutil.StatusDecorator) *client.Snap { 179 localSnap, snapst := about.info, about.snapst 180 result, err := clientutil.ClientSnapFromSnapInfo(localSnap, sd) 181 if err != nil { 182 logger.Noticef("cannot get full app info for snap %q: %v", localSnap.InstanceName(), err) 183 } 184 result.InstalledSize = localSnap.Size 185 186 if icon := snapIcon(localSnap); icon != "" { 187 result.Icon = icon 188 } 189 190 result.Status = "installed" 191 if snapst.Active && localSnap.Revision == snapst.Current { 192 result.Status = "active" 193 } 194 195 result.TrackingChannel = snapst.TrackingChannel 196 result.IgnoreValidation = snapst.IgnoreValidation 197 result.CohortKey = snapst.CohortKey 198 result.DevMode = snapst.DevMode 199 result.TryMode = snapst.TryMode 200 result.JailMode = snapst.JailMode 201 result.MountedFrom = localSnap.MountFile() 202 if result.TryMode { 203 // Readlink instead of EvalSymlinks because it's only expected 204 // to be one level, and should still resolve if the target does 205 // not exist (this might help e.g. snapcraft clean up after a 206 // prime dir) 207 result.MountedFrom, _ = os.Readlink(result.MountedFrom) 208 } 209 result.Health = about.health 210 211 return result 212 } 213 214 // snapIcon tries to find the icon inside the snap 215 func snapIcon(info snap.PlaceInfo) string { 216 found, _ := filepath.Glob(filepath.Join(info.MountDir(), "meta", "gui", "icon.*")) 217 if len(found) == 0 { 218 return "" 219 } 220 221 return found[0] 222 }