github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/sandbox/cgroup/scanning_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 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 cgroup_test 20 21 import ( 22 "bytes" 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/dirs" 31 "github.com/snapcore/snapd/sandbox/cgroup" 32 "github.com/snapcore/snapd/snap/naming" 33 "github.com/snapcore/snapd/testutil" 34 ) 35 36 type scanningSuite struct { 37 testutil.BaseTest 38 rootDir string 39 } 40 41 var _ = Suite(&scanningSuite{}) 42 43 func (s *scanningSuite) SetUpTest(c *C) { 44 s.BaseTest.SetUpTest(c) 45 46 s.rootDir = c.MkDir() 47 dirs.SetRootDir(s.rootDir) 48 s.AddCleanup(func() { dirs.SetRootDir("/") }) 49 } 50 51 func mustParseTag(tag string) naming.SecurityTag { 52 parsedTag, err := naming.ParseSecurityTag(tag) 53 if err != nil { 54 panic(err) 55 } 56 return parsedTag 57 } 58 59 func (s *scanningSuite) TestSecurityTagFromCgroupPath(c *C) { 60 c.Check(cgroup.SecurityTagFromCgroupPath("/a/b/snap.foo.foo.service"), DeepEquals, mustParseTag("snap.foo.foo")) 61 c.Check(cgroup.SecurityTagFromCgroupPath("/a/b/snap.foo.bar.service"), DeepEquals, mustParseTag("snap.foo.bar")) 62 c.Check(cgroup.SecurityTagFromCgroupPath("/a/b/snap.foo.bar.$RANDOM.scope"), DeepEquals, mustParseTag("snap.foo.bar")) 63 c.Check(cgroup.SecurityTagFromCgroupPath("/a/b/snap.foo.hook.bar.$RANDOM.scope"), DeepEquals, mustParseTag("snap.foo.hook.bar")) 64 // We are not confused by snapd things. 65 c.Check(cgroup.SecurityTagFromCgroupPath("/a/b/snap.service"), IsNil) 66 c.Check(cgroup.SecurityTagFromCgroupPath("/a/b/snapd.service"), IsNil) 67 c.Check(cgroup.SecurityTagFromCgroupPath("/a/b/snap.foo.mount"), IsNil) 68 // Real data looks like this. 69 c.Check(cgroup.SecurityTagFromCgroupPath("snap.test-snapd-refresh.sh.d854bd35-2457-4ac8-b494-06061d74df33.scope"), DeepEquals, mustParseTag("snap.test-snapd-refresh.sh")) 70 c.Check(cgroup.SecurityTagFromCgroupPath("snap.test-snapd-refresh.hook.configure.d854bd35-2457-4ac8-b494-06061d74df33.scope"), DeepEquals, mustParseTag("snap.test-snapd-refresh.hook.configure")) 71 // Trailing slashes are automatically handled. 72 c.Check(cgroup.SecurityTagFromCgroupPath("/a/b/snap.foo.foo.service/"), DeepEquals, mustParseTag("snap.foo.foo")) 73 } 74 75 func (s *scanningSuite) writePids(c *C, dir string, pids []int) { 76 var buf bytes.Buffer 77 for _, pid := range pids { 78 fmt.Fprintf(&buf, "%d\n", pid) 79 } 80 81 var path string 82 ver, err := cgroup.Version() 83 c.Assert(err, IsNil) 84 c.Assert(ver == cgroup.V1 || ver == cgroup.V2, Equals, true) 85 switch ver { 86 case cgroup.V1: 87 path = filepath.Join(s.rootDir, "/sys/fs/cgroup/systemd", dir) 88 case cgroup.V2: 89 path = filepath.Join(s.rootDir, "/sys/fs/cgroup", dir) 90 } 91 92 c.Assert(os.MkdirAll(path, 0755), IsNil) 93 c.Assert(ioutil.WriteFile(filepath.Join(path, "cgroup.procs"), buf.Bytes(), 0644), IsNil) 94 } 95 96 func (s *scanningSuite) TestPidsOfSnapEmpty(c *C) { 97 restore := cgroup.MockVersion(cgroup.V1, nil) 98 defer restore() 99 100 // Not having any cgroup directories is not an error. 101 pids, err := cgroup.PidsOfSnap("pkg") 102 c.Assert(err, IsNil) 103 c.Check(pids, HasLen, 0) 104 } 105 106 func (s *scanningSuite) TestPidsOfSnapUnrelatedStuff(c *C) { 107 for _, ver := range []int{cgroup.V2, cgroup.V1} { 108 comment := Commentf("cgroup version %v", ver) 109 restore := cgroup.MockVersion(ver, nil) 110 defer restore() 111 112 // Things that are not related to the snap are not being picked up. 113 s.writePids(c, "udisks2.service", []int{100}) 114 s.writePids(c, "snap..service", []int{101}) 115 s.writePids(c, "snap..scope", []int{102}) 116 s.writePids(c, "snap.*.service", []int{103}) 117 s.writePids(c, "snap.*.scope", []int{104}) 118 s.writePids(c, "snapd.service", []int{105}) 119 s.writePids(c, "snap-spotify-35.mount", []int{106}) 120 121 pids, err := cgroup.PidsOfSnap("pkg") 122 c.Assert(err, IsNil, comment) 123 c.Check(pids, HasLen, 0, comment) 124 } 125 } 126 127 func (s *scanningSuite) TestPidsOfSnapSecurityTags(c *C) { 128 for _, ver := range []int{cgroup.V2, cgroup.V1} { 129 comment := Commentf("cgroup version %v", ver) 130 restore := cgroup.MockVersion(ver, nil) 131 defer restore() 132 133 // Pids are collected and assigned to bins by security tag 134 s.writePids(c, "system.slice/snap.pkg.hook.configure.$RANDOM.scope", []int{1}) 135 s.writePids(c, "system.slice/snap.pkg.daemon.service", []int{2}) 136 137 pids, err := cgroup.PidsOfSnap("pkg") 138 c.Assert(err, IsNil, comment) 139 c.Check(pids, DeepEquals, map[string][]int{ 140 "snap.pkg.hook.configure": {1}, 141 "snap.pkg.daemon": {2}, 142 }, comment) 143 } 144 } 145 146 func (s *scanningSuite) TestPidsOfInstances(c *C) { 147 for _, ver := range []int{cgroup.V2, cgroup.V1} { 148 comment := Commentf("cgroup version %v", ver) 149 restore := cgroup.MockVersion(ver, nil) 150 defer restore() 151 152 // Instances are not confused between themselves and between the non-instance version. 153 s.writePids(c, "system.slice/snap.pkg_prod.daemon.service", []int{1}) 154 s.writePids(c, "system.slice/snap.pkg_devel.daemon.service", []int{2}) 155 s.writePids(c, "system.slice/snap.pkg.daemon.service", []int{3}) 156 157 // The main one 158 pids, err := cgroup.PidsOfSnap("pkg") 159 c.Assert(err, IsNil, comment) 160 c.Check(pids, DeepEquals, map[string][]int{ 161 "snap.pkg.daemon": {3}, 162 }, comment) 163 164 // The development one 165 pids, err = cgroup.PidsOfSnap("pkg_devel") 166 c.Assert(err, IsNil, comment) 167 c.Check(pids, DeepEquals, map[string][]int{ 168 "snap.pkg_devel.daemon": {2}, 169 }, comment) 170 171 // The production one 172 pids, err = cgroup.PidsOfSnap("pkg_prod") 173 c.Assert(err, IsNil, comment) 174 c.Check(pids, DeepEquals, map[string][]int{ 175 "snap.pkg_prod.daemon": {1}, 176 }, comment) 177 } 178 } 179 180 func (s *scanningSuite) TestPidsOfAggregation(c *C) { 181 for _, ver := range []int{cgroup.V2, cgroup.V1} { 182 comment := Commentf("cgroup version %v", ver) 183 restore := cgroup.MockVersion(ver, nil) 184 defer restore() 185 186 // A single snap may be invoked by multiple users in different sessions. 187 // All of their PIDs are collected though. 188 s.writePids(c, "user.slice/user-1000.slice/user@1000.service/gnome-shell-wayland.service/snap.pkg.app.$RANDOM1.scope", []int{1}) // mock 1st invocation 189 s.writePids(c, "user.slice/user-1000.slice/user@1000.service/gnome-shell-wayland.service/snap.pkg.app.$RANDOM2.scope", []int{2}) // mock fork() by pid 1 190 s.writePids(c, "user.slice/user-1001.slice/user@1001.service/gnome-shell-wayland.service/snap.pkg.app.$RANDOM3.scope", []int{3}) // mock 2nd invocation 191 s.writePids(c, "user.slice/user-1001.slice/user@1001.service/gnome-shell-wayland.service/snap.pkg.app.$RANDOM4.scope", []int{4}) // mock fork() by pid 3 192 193 pids, err := cgroup.PidsOfSnap("pkg") 194 c.Assert(err, IsNil, comment) 195 c.Check(pids, DeepEquals, map[string][]int{ 196 "snap.pkg.app": {1, 2, 3, 4}, 197 }, comment) 198 } 199 } 200 201 func (s *scanningSuite) TestPidsOfSnapUnrelated(c *C) { 202 for _, ver := range []int{cgroup.V2, cgroup.V1} { 203 comment := Commentf("cgroup version %v", ver) 204 restore := cgroup.MockVersion(ver, nil) 205 defer restore() 206 207 // We are not confusing snaps with other snaps, instances of our snap, and 208 // with non-snap hierarchies. 209 s.writePids(c, "user.slice/.../snap.pkg.app.$RANDOM1.scope", []int{1}) 210 s.writePids(c, "user.slice/.../snap.other.snap.$RANDOM2.scope", []int{2}) 211 s.writePids(c, "user.slice/.../pkg.service", []int{3}) 212 s.writePids(c, "user.slice/.../snap.pkg_instance.app.$RANDOM3.scope", []int{4}) 213 214 // Write a file which is not cgroup.procs with the number 666 inside. 215 // We want to ensure this is not read by accident. 216 f := filepath.Join(s.rootDir, "/sys/fs/cgroup/unrelated.txt") 217 c.Assert(os.MkdirAll(filepath.Dir(f), 0755), IsNil) 218 c.Assert(ioutil.WriteFile(f, []byte("666"), 0644), IsNil) 219 220 pids, err := cgroup.PidsOfSnap("pkg") 221 c.Assert(err, IsNil, comment) 222 c.Check(pids, DeepEquals, map[string][]int{ 223 "snap.pkg.app": {1}, 224 }, comment) 225 } 226 }