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