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