github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/sandbox/cgroup/tracking_test.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 cgroup_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "strings" 27 28 "github.com/godbus/dbus" 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/dbusutil" 32 "github.com/snapcore/snapd/dbusutil/dbustest" 33 "github.com/snapcore/snapd/dirs" 34 "github.com/snapcore/snapd/features" 35 "github.com/snapcore/snapd/logger" 36 "github.com/snapcore/snapd/sandbox/cgroup" 37 "github.com/snapcore/snapd/testutil" 38 ) 39 40 func enableFeatures(c *C, ff ...features.SnapdFeature) { 41 c.Assert(os.MkdirAll(dirs.FeaturesDir, 0755), IsNil) 42 for _, f := range ff { 43 c.Assert(ioutil.WriteFile(f.ControlFile(), nil, 0755), IsNil) 44 } 45 } 46 47 type trackingSuite struct{} 48 49 var _ = Suite(&trackingSuite{}) 50 51 func (s *trackingSuite) SetUpTest(c *C) { 52 dirs.SetRootDir(c.MkDir()) 53 } 54 55 func (s *trackingSuite) TearDownTest(c *C) { 56 dirs.SetRootDir("") 57 } 58 59 // CreateTransientScopeForTracking always attempts to track, even when refresh app awareness flag is off. 60 func (s *trackingSuite) TestCreateTransientScopeForTrackingFeatureDisabled(c *C) { 61 noDBus := func() (*dbus.Conn, error) { 62 return nil, fmt.Errorf("dbus not available") 63 } 64 restore := dbusutil.MockConnections(noDBus, noDBus) 65 defer restore() 66 67 // The feature is disabled but we still track applications. The feature 68 // flag is now only observed in side snapd snap manager, while considering 69 // snap refreshes. 70 c.Assert(features.RefreshAppAwareness.IsEnabled(), Equals, false) 71 err := cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 72 c.Assert(err, ErrorMatches, "cannot track application process") 73 } 74 75 // CreateTransientScopeForTracking does stuff when refresh app awareness is on 76 func (s *trackingSuite) TestCreateTransientScopeForTrackingFeatureEnabled(c *C) { 77 // Pretend that refresh app awareness is enabled 78 enableFeatures(c, features.RefreshAppAwareness) 79 c.Assert(features.RefreshAppAwareness.IsEnabled(), Equals, true) 80 // Pretend we are a non-root user so that session bus is used. 81 restore := cgroup.MockOsGetuid(12345) 82 defer restore() 83 // Pretend our PID is this value. 84 restore = cgroup.MockOsGetpid(312123) 85 defer restore() 86 // Rig the random UUID generator to return this value. 87 uuid := "cc98cd01-6a25-46bd-b71b-82069b71b770" 88 restore = cgroup.MockRandomUUID(uuid) 89 defer restore() 90 // Replace interactions with DBus so that only session bus is available and responds with our logic. 91 conn, err := dbustest.Connection(func(msg *dbus.Message, n int) ([]*dbus.Message, error) { 92 switch n { 93 case 0: 94 return []*dbus.Message{checkAndRespondToStartTransientUnit(c, msg, "snap.pkg.app."+uuid+".scope", 312123)}, nil 95 } 96 return nil, fmt.Errorf("unexpected message #%d: %s", n, msg) 97 }) 98 c.Assert(err, IsNil) 99 restore = dbusutil.MockOnlySessionBusAvailable(conn) 100 defer restore() 101 // Replace the cgroup analyzer function 102 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 103 return "/user.slice/user-12345.slice/user@12345.service/snap.pkg.app." + uuid + ".scope", nil 104 }) 105 defer restore() 106 107 err = cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 108 c.Check(err, IsNil) 109 } 110 111 func (s *trackingSuite) TestCreateTransientScopeForTrackingUnhappyNotRootGeneric(c *C) { 112 // Pretend that refresh app awareness is enabled 113 enableFeatures(c, features.RefreshAppAwareness) 114 115 // Hand out stub connections to both the system and session bus. 116 // Neither is really used here but they must appear to be available. 117 restore := dbusutil.MockConnections(dbustest.StubConnection, dbustest.StubConnection) 118 defer restore() 119 120 // Pretend we are a non-root user so that session bus is used. 121 restore = cgroup.MockOsGetuid(12345) 122 defer restore() 123 // Pretend our PID is this value. 124 restore = cgroup.MockOsGetpid(312123) 125 defer restore() 126 127 // Rig the cgroup analyzer to return an answer not related to the snap name. 128 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 129 return "foo", nil 130 }) 131 defer restore() 132 133 // Pretend that attempting to create a transient scope fails with a canned error. 134 restore = cgroup.MockDoCreateTransientScope(func(conn *dbus.Conn, unitName string, pid int) error { 135 return fmt.Errorf("cannot create transient scope for testing") 136 }) 137 defer restore() 138 139 // Create a transient scope and see it fail according to how doCreateTransientScope is rigged. 140 err := cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 141 c.Assert(err, ErrorMatches, "cannot create transient scope for testing") 142 143 // Calling StartTransientUnit fails with org.freedesktop.DBus.UnknownMethod error. 144 // This is possible on old systemd or on deputy systemd. 145 restore = cgroup.MockDoCreateTransientScope(func(conn *dbus.Conn, unitName string, pid int) error { 146 return cgroup.ErrDBusUnknownMethod 147 }) 148 defer restore() 149 150 // Attempts to create a transient scope fail with a special error 151 // indicating that we cannot track application process. 152 err = cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 153 c.Assert(err, ErrorMatches, "cannot track application process") 154 155 // Calling StartTransientUnit fails with org.freedesktop.DBus.Spawn.ChildExited error. 156 // This is possible where we try to activate socket activate session bus 157 // but it's not available OR when we try to socket activate systemd --user. 158 restore = cgroup.MockDoCreateTransientScope(func(conn *dbus.Conn, unitName string, pid int) error { 159 return cgroup.ErrDBusSpawnChildExited 160 }) 161 defer restore() 162 163 // Attempts to create a transient scope fail with a special error 164 // indicating that we cannot track application process and because we are 165 // not root, we do not attempt to fall back to the system bus. 166 err = cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 167 c.Assert(err, ErrorMatches, "cannot track application process") 168 } 169 170 func (s *trackingSuite) TestCreateTransientScopeForTrackingUnhappyRootFallback(c *C) { 171 // Pretend that refresh app awareness is enabled 172 enableFeatures(c, features.RefreshAppAwareness) 173 174 // Hand out stub connections to both the system and session bus. 175 // Neither is really used here but they must appear to be available. 176 restore := dbusutil.MockConnections(dbustest.StubConnection, dbustest.StubConnection) 177 defer restore() 178 179 // Pretend we are a root user so that we attempt to use the system bus as fallback. 180 restore = cgroup.MockOsGetuid(0) 181 defer restore() 182 // Pretend our PID is this value. 183 restore = cgroup.MockOsGetpid(312123) 184 defer restore() 185 186 // Rig the random UUID generator to return this value. 187 uuid := "cc98cd01-6a25-46bd-b71b-82069b71b770" 188 restore = cgroup.MockRandomUUID(uuid) 189 defer restore() 190 191 // Calling StartTransientUnit fails on the session and then works on the system bus. 192 // This test emulates a root user falling back from the session bus to the system bus. 193 n := 0 194 restore = cgroup.MockDoCreateTransientScope(func(conn *dbus.Conn, unitName string, pid int) error { 195 n++ 196 switch n { 197 case 1: 198 // On first try we fail. This is when we used the session bus/ 199 return cgroup.ErrDBusSpawnChildExited 200 case 2: 201 // On second try we succeed. 202 return nil 203 } 204 panic("expected to call doCreateTransientScope at most twice") 205 }) 206 defer restore() 207 208 // Rig the cgroup analyzer to pretend that we got placed into the system slice. 209 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 210 c.Assert(pid, Equals, 312123) 211 return "/system.slice/snap.pkg.app." + uuid + ".scope", nil 212 }) 213 defer restore() 214 215 // Attempts to create a transient scope fail with a special error 216 // indicating that we cannot track application process and but because we were 217 // root we attempted to fall back to the system bus. 218 err := cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 219 c.Assert(err, IsNil) 220 } 221 222 func (s *trackingSuite) TestCreateTransientScopeForTrackingUnhappyRootFailedFallback(c *C) { 223 // Pretend that refresh app awareness is enabled 224 enableFeatures(c, features.RefreshAppAwareness) 225 226 // Make it appear that session bus is there but system bus is not. 227 noSystemBus := func() (*dbus.Conn, error) { 228 return nil, fmt.Errorf("system bus is not available for testing") 229 } 230 restore := dbusutil.MockConnections(noSystemBus, dbustest.StubConnection) 231 defer restore() 232 233 // Pretend we are a root user so that we attempt to use the system bus as fallback. 234 restore = cgroup.MockOsGetuid(0) 235 defer restore() 236 // Pretend our PID is this value. 237 restore = cgroup.MockOsGetpid(312123) 238 defer restore() 239 240 // Rig the random UUID generator to return this value. 241 uuid := "cc98cd01-6a25-46bd-b71b-82069b71b770" 242 restore = cgroup.MockRandomUUID(uuid) 243 defer restore() 244 245 // Calling StartTransientUnit fails so that we try to use the system bus as fallback. 246 restore = cgroup.MockDoCreateTransientScope(func(conn *dbus.Conn, unitName string, pid int) error { 247 return cgroup.ErrDBusSpawnChildExited 248 }) 249 defer restore() 250 251 // Rig the cgroup analyzer to return an answer not related to the snap name. 252 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 253 return "foo", nil 254 }) 255 defer restore() 256 257 // Attempts to create a transient scope fail with a special error 258 // indicating that we cannot track application process. 259 err := cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 260 c.Assert(err, ErrorMatches, "cannot track application process") 261 } 262 263 func (s *trackingSuite) TestCreateTransientScopeForTrackingUnhappyNoDBus(c *C) { 264 // Pretend that refresh app awareness is enabled 265 enableFeatures(c, features.RefreshAppAwareness) 266 267 // Make it appear that DBus is entirely unavailable. 268 noBus := func() (*dbus.Conn, error) { 269 return nil, fmt.Errorf("dbus is not available for testing") 270 } 271 restore := dbusutil.MockConnections(noBus, noBus) 272 defer restore() 273 274 // Pretend we are a root user so that we attempt to use the system bus as fallback. 275 restore = cgroup.MockOsGetuid(0) 276 defer restore() 277 // Pretend our PID is this value. 278 restore = cgroup.MockOsGetpid(312123) 279 defer restore() 280 281 // Rig the random UUID generator to return this value. 282 uuid := "cc98cd01-6a25-46bd-b71b-82069b71b770" 283 restore = cgroup.MockRandomUUID(uuid) 284 defer restore() 285 286 // Calling StartTransientUnit is not attempted without a DBus connection. 287 restore = cgroup.MockDoCreateTransientScope(func(conn *dbus.Conn, unitName string, pid int) error { 288 c.Error("test sequence violated") 289 return fmt.Errorf("test was not expected to create a transient scope") 290 }) 291 defer restore() 292 293 // Disable the cgroup analyzer function as we don't expect it to be used in this test. 294 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 295 c.Error("test sequence violated") 296 return "", fmt.Errorf("test was not expected to measure process path in the tracking cgroup") 297 }) 298 defer restore() 299 300 // Attempts to create a transient scope fail with a special error 301 // indicating that we cannot track application process. 302 err := cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 303 c.Assert(err, ErrorMatches, "cannot track application process") 304 } 305 306 func (s *trackingSuite) TestCreateTransientScopeForTrackingSilentlyFails(c *C) { 307 // Pretend that refresh app awareness is enabled 308 enableFeatures(c, features.RefreshAppAwareness) 309 310 // Hand out stub connections to both the system and session bus. 311 // Neither is really used here but they must appear to be available. 312 restore := dbusutil.MockConnections(dbustest.StubConnection, dbustest.StubConnection) 313 defer restore() 314 315 // Pretend we are a non-root user. 316 restore = cgroup.MockOsGetuid(12345) 317 defer restore() 318 // Pretend our PID is this value. 319 restore = cgroup.MockOsGetpid(312123) 320 defer restore() 321 322 // Rig the random UUID generator to return this value. 323 uuid := "cc98cd01-6a25-46bd-b71b-82069b71b770" 324 restore = cgroup.MockRandomUUID(uuid) 325 defer restore() 326 327 // Calling StartTransientUnit succeeds but in reality does not move our 328 // process to the new cgroup hierarchy. This can happen when systemd 329 // version is < 238 and when the calling user is in a hierarchy that is 330 // owned by another user. One example is a user logging in remotely over 331 // ssh. 332 restore = cgroup.MockDoCreateTransientScope(func(conn *dbus.Conn, unitName string, pid int) error { 333 return nil 334 }) 335 defer restore() 336 337 // Rig the cgroup analyzer to pretend that we are not placed in a snap-related slice. 338 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 339 c.Assert(pid, Equals, 312123) 340 return "/system.slice/foo.service", nil 341 }) 342 defer restore() 343 344 // Attempts to create a transient scope fail with a special error 345 // indicating that we cannot track application process even though 346 // the DBus call has returned no error. 347 err := cgroup.CreateTransientScopeForTracking("snap.pkg.app", nil) 348 c.Assert(err, ErrorMatches, "cannot track application process") 349 } 350 351 func (s *trackingSuite) TestCreateTransientScopeForRootOnSystemBus(c *C) { 352 // Pretend that refresh app awareness is enabled 353 enableFeatures(c, features.RefreshAppAwareness) 354 355 // Hand out stub connections to both the system and session bus. Remember 356 // the identity of the system bus to that we can verify access later. 357 // Neither is really used here but they must appear to be available. 358 systemBus, err := dbustest.StubConnection() 359 c.Assert(err, IsNil) 360 restore := dbusutil.MockConnections(func() (*dbus.Conn, error) { return systemBus, nil }, dbustest.StubConnection) 361 defer restore() 362 363 // Pretend we are a root user. All hooks execute as root. 364 restore = cgroup.MockOsGetuid(0) 365 defer restore() 366 367 // Pretend our PID is this value. 368 restore = cgroup.MockOsGetpid(312123) 369 defer restore() 370 371 // Rig the random UUID generator to return this value. 372 uuid := "cc98cd01-6a25-46bd-b71b-82069b71b770" 373 restore = cgroup.MockRandomUUID(uuid) 374 defer restore() 375 376 // Pretend that attempting to create a transient scope succeeds. Measure 377 // the bus used and the unit name provided by the caller. Note that the 378 // call was made on the system bus, as requested by TrackingOptions below. 379 restore = cgroup.MockDoCreateTransientScope(func(conn *dbus.Conn, unitName string, pid int) error { 380 c.Assert(conn, Equals, systemBus) 381 c.Assert(unitName, Equals, "snap.pkg.app."+uuid+".scope") 382 return nil 383 }) 384 defer restore() 385 386 // Rig the cgroup analyzer to indicate successful tracking. 387 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 388 return "snap.pkg.app." + uuid + ".scope", nil 389 }) 390 defer restore() 391 392 // Create a transient scope and see it succeed. 393 err = cgroup.CreateTransientScopeForTracking("snap.pkg.app", &cgroup.TrackingOptions{AllowSessionBus: false}) 394 c.Assert(err, IsNil) 395 } 396 397 func checkAndRespondToStartTransientUnit(c *C, msg *dbus.Message, scopeName string, pid int) *dbus.Message { 398 // XXX: Those types might live in a package somewhere 399 type Property struct { 400 Name string 401 Value interface{} 402 } 403 type Unit struct { 404 Name string 405 Props []Property 406 } 407 // Signature of StartTransientUnit, string, string, array of Property and array of Unit (see above). 408 requestSig := dbus.SignatureOf("", "", []Property{}, []Unit{}) 409 410 c.Assert(msg.Type, Equals, dbus.TypeMethodCall) 411 c.Check(msg.Flags, Equals, dbus.Flags(0)) 412 c.Check(msg.Headers, DeepEquals, map[dbus.HeaderField]dbus.Variant{ 413 dbus.FieldDestination: dbus.MakeVariant("org.freedesktop.systemd1"), 414 dbus.FieldPath: dbus.MakeVariant(dbus.ObjectPath("/org/freedesktop/systemd1")), 415 dbus.FieldInterface: dbus.MakeVariant("org.freedesktop.systemd1.Manager"), 416 dbus.FieldMember: dbus.MakeVariant("StartTransientUnit"), 417 dbus.FieldSignature: dbus.MakeVariant(requestSig), 418 }) 419 c.Check(msg.Body, DeepEquals, []interface{}{ 420 scopeName, 421 "fail", 422 [][]interface{}{ 423 {"PIDs", dbus.MakeVariant([]uint32{uint32(pid)})}, 424 }, 425 [][]interface{}{}, 426 }) 427 428 responseSig := dbus.SignatureOf(dbus.ObjectPath("")) 429 return &dbus.Message{ 430 Type: dbus.TypeMethodReply, 431 Headers: map[dbus.HeaderField]dbus.Variant{ 432 dbus.FieldReplySerial: dbus.MakeVariant(msg.Serial()), 433 dbus.FieldSender: dbus.MakeVariant(":1"), // This does not matter. 434 // dbus.FieldDestination is provided automatically by DBus test helper. 435 dbus.FieldSignature: dbus.MakeVariant(responseSig), 436 }, 437 // The object path returned in the body is not used by snap run yet. 438 Body: []interface{}{dbus.ObjectPath("/org/freedesktop/systemd1/job/1462")}, 439 } 440 } 441 442 func checkAndFailToStartTransientUnit(c *C, msg *dbus.Message, errMsg string) *dbus.Message { 443 c.Assert(msg.Type, Equals, dbus.TypeMethodCall) 444 // ignore the message and just produce an error response 445 return &dbus.Message{ 446 Type: dbus.TypeError, 447 Headers: map[dbus.HeaderField]dbus.Variant{ 448 dbus.FieldReplySerial: dbus.MakeVariant(msg.Serial()), 449 dbus.FieldSender: dbus.MakeVariant(":1"), // This does not matter. 450 // dbus.FieldDestination is provided automatically by DBus test helper. 451 dbus.FieldErrorName: dbus.MakeVariant(errMsg), 452 }, 453 } 454 } 455 456 func (s *trackingSuite) TestDoCreateTransientScopeHappy(c *C) { 457 conn, err := dbustest.Connection(func(msg *dbus.Message, n int) ([]*dbus.Message, error) { 458 switch n { 459 case 0: 460 return []*dbus.Message{checkAndRespondToStartTransientUnit(c, msg, "foo.scope", 312123)}, nil 461 } 462 return nil, fmt.Errorf("unexpected message #%d: %s", n, msg) 463 }) 464 465 c.Assert(err, IsNil) 466 defer conn.Close() 467 err = cgroup.DoCreateTransientScope(conn, "foo.scope", 312123) 468 c.Assert(err, IsNil) 469 } 470 471 func (s *trackingSuite) TestDoCreateTransientScopeForwardedErrors(c *C) { 472 // Certain errors are forwarded and handled in the logic calling into 473 // DoCreateTransientScope. Those are tested here. 474 for _, t := range []struct { 475 dbusError, msg string 476 }{ 477 {"org.freedesktop.DBus.Error.NameHasNoOwner", "dbus name has no owner"}, 478 {"org.freedesktop.DBus.Error.UnknownMethod", "unknown dbus object method"}, 479 {"org.freedesktop.DBus.Error.Spawn.ChildExited", "dbus spawned child process exited"}, 480 } { 481 conn, err := dbustest.Connection(func(msg *dbus.Message, n int) ([]*dbus.Message, error) { 482 switch n { 483 case 0: 484 return []*dbus.Message{checkAndFailToStartTransientUnit(c, msg, t.dbusError)}, nil 485 } 486 return nil, fmt.Errorf("unexpected message #%d: %s", n, msg) 487 }) 488 c.Assert(err, IsNil) 489 defer conn.Close() 490 err = cgroup.DoCreateTransientScope(conn, "foo.scope", 312123) 491 c.Assert(strings.HasSuffix(err.Error(), fmt.Sprintf(" [%s]", t.dbusError)), Equals, true, Commentf("%q ~ %s", err, t.dbusError)) 492 c.Check(err, ErrorMatches, t.msg+" .*") 493 } 494 } 495 496 func (s *trackingSuite) TestDoCreateTransientScopeClashingScopeName(c *C) { 497 // In case our UUID algorithm is bad and systemd reports that an unit with 498 // identical name already exists, we provide a special error handler for that. 499 errMsg := "org.freedesktop.systemd1.UnitExists" 500 conn, err := dbustest.Connection(func(msg *dbus.Message, n int) ([]*dbus.Message, error) { 501 switch n { 502 case 0: 503 return []*dbus.Message{checkAndFailToStartTransientUnit(c, msg, errMsg)}, nil 504 } 505 return nil, fmt.Errorf("unexpected message #%d: %s", n, msg) 506 }) 507 c.Assert(err, IsNil) 508 defer conn.Close() 509 err = cgroup.DoCreateTransientScope(conn, "foo.scope", 312123) 510 c.Assert(err, ErrorMatches, "cannot create transient scope: scope .* clashed: .*") 511 } 512 513 func (s *trackingSuite) TestDoCreateTransientScopeOtherDBusErrors(c *C) { 514 // Other DBus errors are not special-cased and cause a generic failure handler. 515 errMsg := "org.example.BadHairDay" 516 conn, err := dbustest.Connection(func(msg *dbus.Message, n int) ([]*dbus.Message, error) { 517 switch n { 518 case 0: 519 return []*dbus.Message{checkAndFailToStartTransientUnit(c, msg, errMsg)}, nil 520 } 521 return nil, fmt.Errorf("unexpected message #%d: %s", n, msg) 522 }) 523 c.Assert(err, IsNil) 524 defer conn.Close() 525 err = cgroup.DoCreateTransientScope(conn, "foo.scope", 312123) 526 c.Assert(err, ErrorMatches, `cannot create transient scope: DBus error "org.example.BadHairDay": \[\]`) 527 } 528 529 func (s *trackingSuite) TestSessionOrMaybeSystemBusTotalFailureForRoot(c *C) { 530 system := func() (*dbus.Conn, error) { 531 return nil, fmt.Errorf("system bus unavailable for testing") 532 } 533 session := func() (*dbus.Conn, error) { 534 return nil, fmt.Errorf("session bus unavailable for testing") 535 } 536 restore := dbusutil.MockConnections(system, session) 537 defer restore() 538 logBuf, restore := logger.MockLogger() 539 defer restore() 540 os.Setenv("SNAPD_DEBUG", "true") 541 defer os.Unsetenv("SNAPD_DEBUG") 542 543 uid := 0 544 isSession, conn, err := cgroup.SessionOrMaybeSystemBus(uid) 545 c.Assert(err, ErrorMatches, "system bus unavailable for testing") 546 c.Check(conn, IsNil) 547 c.Check(isSession, Equals, false) 548 c.Check(logBuf.String(), testutil.Contains, "DEBUG: session bus is not available: session bus unavailable for testing\n") 549 c.Check(logBuf.String(), testutil.Contains, "DEBUG: falling back to system bus\n") 550 c.Check(logBuf.String(), testutil.Contains, "DEBUG: system bus is not available: system bus unavailable for testing\n") 551 } 552 553 func (s *trackingSuite) TestSessionOrMaybeSystemBusFallbackForRoot(c *C) { 554 system := func() (*dbus.Conn, error) { 555 return dbustest.StubConnection() 556 } 557 session := func() (*dbus.Conn, error) { 558 return nil, fmt.Errorf("session bus unavailable for testing") 559 } 560 restore := dbusutil.MockConnections(system, session) 561 defer restore() 562 logBuf, restore := logger.MockLogger() 563 defer restore() 564 os.Setenv("SNAPD_DEBUG", "true") 565 defer os.Unsetenv("SNAPD_DEBUG") 566 567 uid := 0 568 isSession, conn, err := cgroup.SessionOrMaybeSystemBus(uid) 569 c.Assert(err, IsNil) 570 conn.Close() 571 c.Check(isSession, Equals, false) 572 c.Check(logBuf.String(), testutil.Contains, "DEBUG: session bus is not available: session bus unavailable for testing\n") 573 c.Check(logBuf.String(), testutil.Contains, "DEBUG: falling back to system bus\n") 574 c.Check(logBuf.String(), testutil.Contains, "DEBUG: using system bus now, session bus was not available\n") 575 } 576 577 func (s *trackingSuite) TestSessionOrMaybeSystemBusNonRootSessionFailure(c *C) { 578 system := func() (*dbus.Conn, error) { 579 return dbustest.StubConnection() 580 } 581 session := func() (*dbus.Conn, error) { 582 return nil, fmt.Errorf("session bus unavailable for testing") 583 } 584 restore := dbusutil.MockConnections(system, session) 585 defer restore() 586 logBuf, restore := logger.MockLogger() 587 defer restore() 588 os.Setenv("SNAPD_DEBUG", "true") 589 defer os.Unsetenv("SNAPD_DEBUG") 590 591 uid := 12345 592 isSession, conn, err := cgroup.SessionOrMaybeSystemBus(uid) 593 c.Assert(err, ErrorMatches, "session bus unavailable for testing") 594 c.Check(conn, IsNil) 595 c.Check(isSession, Equals, false) 596 c.Check(logBuf.String(), testutil.Contains, "DEBUG: session bus is not available: session bus unavailable for testing\n") 597 } 598 599 func (s *trackingSuite) TestConfirmSystemdServiceTrackingHappy(c *C) { 600 // Pretend our PID is this value. 601 restore := cgroup.MockOsGetpid(312123) 602 defer restore() 603 // Replace the cgroup analyzer function 604 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 605 c.Assert(pid, Equals, 312123) 606 return "/user.slice/user-12345.slice/user@12345.service/snap.pkg.app.service", nil 607 }) 608 defer restore() 609 610 // With the cgroup path faked as above, we are being tracked as the systemd 611 // service so no error is reported. 612 err := cgroup.ConfirmSystemdServiceTracking("snap.pkg.app") 613 c.Assert(err, IsNil) 614 } 615 616 func (s *trackingSuite) TestConfirmSystemdServiceTrackingSad(c *C) { 617 // Pretend our PID is this value. 618 restore := cgroup.MockOsGetpid(312123) 619 defer restore() 620 // Replace the cgroup analyzer function 621 restore = cgroup.MockCgroupProcessPathInTrackingCgroup(func(pid int) (string, error) { 622 c.Assert(pid, Equals, 312123) 623 // Tracking path of a gnome terminal helper process. Meant to illustrate a tracking but not related to a snap application. 624 return "user.slice/user-12345.slice/user@12345.service/apps.slice/apps-org.gnome.Terminal.slice/vte-spawn-e640104a-cddf-4bd8-ba4b-2c1baf0270c3.scope", nil 625 }) 626 defer restore() 627 628 // With the cgroup path faked as above, tracking is not effective. 629 err := cgroup.ConfirmSystemdServiceTracking("snap.pkg.app") 630 c.Assert(err, Equals, cgroup.ErrCannotTrackProcess) 631 }