gitee.com/mysnapcore/mysnapd@v0.1.0/cmd/snap/cmd_run_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2022 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 main_test 21 22 import ( 23 "encoding/json" 24 "errors" 25 "fmt" 26 "io/ioutil" 27 "net/http" 28 "os" 29 "os/user" 30 "path/filepath" 31 "strings" 32 "time" 33 34 "gopkg.in/check.v1" 35 36 snaprun "gitee.com/mysnapcore/mysnapd/cmd/snap" 37 "gitee.com/mysnapcore/mysnapd/cmd/snaplock/runinhibit" 38 "gitee.com/mysnapcore/mysnapd/dirs" 39 "gitee.com/mysnapcore/mysnapd/features" 40 "gitee.com/mysnapcore/mysnapd/logger" 41 "gitee.com/mysnapcore/mysnapd/osutil" 42 "gitee.com/mysnapcore/mysnapd/osutil/strace" 43 "gitee.com/mysnapcore/mysnapd/progress" 44 "gitee.com/mysnapcore/mysnapd/progress/progresstest" 45 "gitee.com/mysnapcore/mysnapd/sandbox/cgroup" 46 "gitee.com/mysnapcore/mysnapd/sandbox/selinux" 47 "gitee.com/mysnapcore/mysnapd/snap" 48 "gitee.com/mysnapcore/mysnapd/snap/snaptest" 49 "gitee.com/mysnapcore/mysnapd/testutil" 50 usersessionclient "gitee.com/mysnapcore/mysnapd/usersession/client" 51 "gitee.com/mysnapcore/mysnapd/x11" 52 ) 53 54 var mockYaml = []byte(`name: snapname 55 version: 1.0 56 apps: 57 app: 58 command: run-app 59 svc: 60 command: run-svc 61 daemon: simple 62 hooks: 63 configure: 64 `) 65 66 var mockYamlBaseNone1 = []byte(`name: snapname1 67 version: 1.0 68 base: none 69 apps: 70 app: 71 command: run-app 72 `) 73 74 var mockYamlBaseNone2 = []byte(`name: snapname2 75 version: 1.0 76 base: none 77 hooks: 78 configure: 79 `) 80 81 type RunSuite struct { 82 fakeHome string 83 BaseSnapSuite 84 } 85 86 var _ = check.Suite(&RunSuite{}) 87 88 func (s *RunSuite) SetUpTest(c *check.C) { 89 s.BaseSnapSuite.SetUpTest(c) 90 s.fakeHome = c.MkDir() 91 92 u, err := user.Current() 93 c.Assert(err, check.IsNil) 94 s.AddCleanup(snaprun.MockUserCurrent(func() (*user.User, error) { 95 return &user.User{Uid: u.Uid, HomeDir: s.fakeHome}, nil 96 })) 97 s.AddCleanup(snaprun.MockCreateTransientScopeForTracking(func(string, *cgroup.TrackingOptions) error { 98 return nil 99 })) 100 restoreIsGraphicalSession := snaprun.MockIsGraphicalSession(false) 101 s.AddCleanup(restoreIsGraphicalSession) 102 } 103 104 func (s *RunSuite) TestInvalidParameters(c *check.C) { 105 invalidParameters := []string{"run", "--hook=configure", "--command=command-name", "--", "snap-name"} 106 _, err := snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 107 c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*") 108 109 invalidParameters = []string{"run", "--hook=configure", "--timer=10:00-12:00", "--", "snap-name"} 110 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 111 c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*") 112 113 invalidParameters = []string{"run", "--command=command-name", "--timer=10:00-12:00", "--", "snap-name"} 114 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 115 c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*") 116 117 invalidParameters = []string{"run", "-r=1", "--command=command-name", "--", "snap-name"} 118 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 119 c.Check(err, check.ErrorMatches, ".*-r can only be used with --hook.*") 120 121 invalidParameters = []string{"run", "-r=1", "--", "snap-name"} 122 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 123 c.Check(err, check.ErrorMatches, ".*-r can only be used with --hook.*") 124 125 invalidParameters = []string{"run", "--hook=configure", "--", "foo", "bar", "snap-name"} 126 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 127 c.Check(err, check.ErrorMatches, ".*too many arguments for hook \"configure\": bar.*") 128 } 129 130 func (s *RunSuite) TestRunCmdWithBaseNone(c *check.C) { 131 defer mockSnapConfine(dirs.DistroLibExecDir)() 132 133 // mock installed snap 134 snaptest.MockSnapCurrent(c, string(mockYamlBaseNone1), &snap.SideInfo{ 135 Revision: snap.R("1"), 136 }) 137 snaptest.MockSnapCurrent(c, string(mockYamlBaseNone2), &snap.SideInfo{ 138 Revision: snap.R("1"), 139 }) 140 141 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname1.app", "--arg1", "arg2"}) 142 c.Assert(err, check.ErrorMatches, `cannot run hooks / applications with base \"none\"`) 143 144 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname2"}) 145 c.Assert(err, check.ErrorMatches, `cannot run hooks / applications with base \"none\"`) 146 } 147 148 func (s *RunSuite) TestSnapRunWhenMissingConfine(c *check.C) { 149 _, r := logger.MockLogger() 150 defer r() 151 152 // mock installed snap 153 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 154 Revision: snap.R("x2"), 155 }) 156 157 // redirect exec 158 var execs [][]string 159 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 160 execs = append(execs, args) 161 return nil 162 }) 163 defer restorer() 164 165 // and run it! 166 // a regular run will fail 167 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 168 c.Assert(err, check.ErrorMatches, `.* your core/snapd package`) 169 // a hook run will not fail 170 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"}) 171 c.Assert(err, check.IsNil) 172 173 // but nothing is run ever 174 c.Check(execs, check.IsNil) 175 } 176 177 func (s *RunSuite) TestSnapRunAppIntegration(c *check.C) { 178 defer mockSnapConfine(dirs.DistroLibExecDir)() 179 180 tmpdir := os.Getenv("TMPDIR") 181 if tmpdir == "" { 182 tmpdir = "/var/tmp" 183 os.Setenv("TMPDIR", tmpdir) 184 defer os.Unsetenv("TMPDIR") 185 } 186 187 // mock installed snap 188 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 189 Revision: snap.R("x2"), 190 }) 191 192 // redirect exec 193 execArg0 := "" 194 execArgs := []string{} 195 execEnv := []string{} 196 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 197 execArg0 = arg0 198 execArgs = args 199 execEnv = envv 200 return nil 201 }) 202 defer restorer() 203 204 // and run it! 205 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 206 c.Assert(err, check.IsNil) 207 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 208 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 209 c.Check(execArgs, check.DeepEquals, []string{ 210 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 211 "snap.snapname.app", 212 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 213 "snapname.app", "--arg1", "arg2"}) 214 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 215 c.Check(execEnv, testutil.Contains, fmt.Sprintf("TMPDIR=%s", tmpdir)) 216 } 217 218 func (s *RunSuite) TestSnapRunAppRunsChecksInhibitionLock(c *check.C) { 219 defer mockSnapConfine(dirs.DistroLibExecDir)() 220 221 // mock installed snap 222 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{Revision: snap.R("x2")}) 223 224 var execArg0 string 225 var execArgs []string 226 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 227 execArg0 = arg0 228 execArgs = args 229 return nil 230 }) 231 defer restorer() 232 233 c.Assert(runinhibit.LockWithHint("snapname", runinhibit.HintInhibitedForRefresh), check.IsNil) 234 c.Assert(os.MkdirAll(dirs.FeaturesDir, 0755), check.IsNil) 235 c.Assert(ioutil.WriteFile(features.RefreshAppAwareness.ControlFile(), []byte(nil), 0644), check.IsNil) 236 237 var called int 238 restore := snaprun.MockWaitInhibitUnlock(func(snapName string, waitFor runinhibit.Hint) (bool, error) { 239 called++ 240 return false, nil 241 }) 242 defer restore() 243 244 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1"}) 245 c.Assert(err, check.IsNil) 246 c.Check(called, check.Equals, 2) 247 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1"}) 248 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 249 c.Check(execArgs, check.DeepEquals, []string{ 250 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 251 "snap.snapname.app", 252 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 253 "snapname.app", "--arg1"}) 254 } 255 256 func (s *RunSuite) TestSnapRunHookNoRuninhibit(c *check.C) { 257 defer mockSnapConfine(dirs.DistroLibExecDir)() 258 259 // mock installed snap 260 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 261 Revision: snap.R(42), 262 }) 263 264 // redirect exec 265 execArg0 := "" 266 execArgs := []string{} 267 execEnv := []string{} 268 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 269 execArg0 = arg0 270 execArgs = args 271 execEnv = envv 272 return nil 273 }) 274 defer restorer() 275 276 var called bool 277 restore := snaprun.MockWaitInhibitUnlock(func(snapName string, waitFor runinhibit.Hint) (bool, error) { 278 called = true 279 c.Errorf("WaitInhibitUnlock should not have been called") 280 return false, nil 281 }) 282 defer restore() 283 284 c.Assert(runinhibit.LockWithHint("snapname", runinhibit.HintInhibitedForRefresh), check.IsNil) 285 c.Assert(os.MkdirAll(dirs.FeaturesDir, 0755), check.IsNil) 286 c.Assert(ioutil.WriteFile(features.RefreshAppAwareness.ControlFile(), []byte(nil), 0644), check.IsNil) 287 288 // Run a hook from the active revision 289 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"}) 290 c.Assert(err, check.IsNil) 291 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 292 c.Check(execArgs, check.DeepEquals, []string{ 293 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 294 "snap.snapname.hook.configure", 295 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 296 "--hook=configure", "snapname"}) 297 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 298 c.Check(called, check.Equals, false) 299 } 300 301 func (s *RunSuite) TestSnapRunAppRuninhibitSkipsServices(c *check.C) { 302 defer mockSnapConfine(dirs.DistroLibExecDir)() 303 304 // mock installed snap 305 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{Revision: snap.R("x2")}) 306 307 var execArg0 string 308 var execArgs []string 309 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 310 execArg0 = arg0 311 execArgs = args 312 return nil 313 }) 314 defer restorer() 315 316 c.Assert(runinhibit.LockWithHint("snapname", runinhibit.HintInhibitedForRefresh), check.IsNil) 317 c.Assert(os.MkdirAll(dirs.FeaturesDir, 0755), check.IsNil) 318 c.Assert(ioutil.WriteFile(features.RefreshAppAwareness.ControlFile(), []byte(nil), 0644), check.IsNil) 319 320 var called bool 321 restore := snaprun.MockWaitInhibitUnlock(func(snapName string, waitFor runinhibit.Hint) (bool, error) { 322 called = true 323 c.Errorf("WaitInhibitUnlock should not have been called") 324 return false, nil 325 }) 326 defer restore() 327 328 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 329 c.Assert(securityTag, check.Equals, "snap.snapname.svc") 330 return nil 331 }) 332 defer restore() 333 334 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.svc"}) 335 c.Assert(err, check.IsNil) 336 c.Check(called, check.Equals, false) 337 c.Assert(rest, check.DeepEquals, []string{"snapname.svc"}) 338 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 339 c.Check(execArgs, check.DeepEquals, []string{ 340 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), "snap.snapname.svc", 341 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), "snapname.svc"}) 342 } 343 344 func (s *RunSuite) TestSnapRunClassicAppIntegration(c *check.C) { 345 defer mockSnapConfine(dirs.DistroLibExecDir)() 346 347 tmpdir := os.Getenv("TMPDIR") 348 if tmpdir == "" { 349 tmpdir = "/var/tmp" 350 os.Setenv("TMPDIR", tmpdir) 351 defer os.Unsetenv("TMPDIR") 352 } 353 354 // mock installed snap 355 snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{ 356 Revision: snap.R("x2"), 357 }) 358 359 // redirect exec 360 execArg0 := "" 361 execArgs := []string{} 362 execEnv := []string{} 363 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 364 execArg0 = arg0 365 execArgs = args 366 execEnv = envv 367 return nil 368 }) 369 defer restorer() 370 371 // and run it! 372 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 373 c.Assert(err, check.IsNil) 374 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 375 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 376 c.Check(execArgs, check.DeepEquals, []string{ 377 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), "--classic", 378 "snap.snapname.app", 379 filepath.Join(dirs.DistroLibExecDir, "snap-exec"), 380 "snapname.app", "--arg1", "arg2"}) 381 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 382 c.Check(execEnv, testutil.Contains, fmt.Sprintf("SNAP_SAVED_TMPDIR=%s", tmpdir)) 383 } 384 385 func (s *RunSuite) TestSnapRunClassicAppIntegrationReexecedFromCore(c *check.C) { 386 mountedCorePath := filepath.Join(dirs.SnapMountDir, "core/current") 387 mountedCoreLibExecPath := filepath.Join(mountedCorePath, dirs.CoreLibExecDir) 388 389 defer mockSnapConfine(mountedCoreLibExecPath)() 390 391 // mock installed snap 392 snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{ 393 Revision: snap.R("x2"), 394 }) 395 396 restore := snaprun.MockOsReadlink(func(name string) (string, error) { 397 // pretend 'snap' is reexeced from 'core' 398 return filepath.Join(mountedCorePath, "usr/bin/snap"), nil 399 }) 400 defer restore() 401 402 execArgs := []string{} 403 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 404 execArgs = args 405 return nil 406 }) 407 defer restorer() 408 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 409 c.Assert(err, check.IsNil) 410 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 411 c.Check(execArgs, check.DeepEquals, []string{ 412 filepath.Join(mountedCoreLibExecPath, "snap-confine"), "--classic", 413 "snap.snapname.app", 414 filepath.Join(mountedCoreLibExecPath, "snap-exec"), 415 "snapname.app", "--arg1", "arg2"}) 416 } 417 418 func (s *RunSuite) TestSnapRunClassicAppIntegrationReexecedFromSnapd(c *check.C) { 419 mountedSnapdPath := filepath.Join(dirs.SnapMountDir, "snapd/current") 420 mountedSnapdLibExecPath := filepath.Join(mountedSnapdPath, dirs.CoreLibExecDir) 421 422 defer mockSnapConfine(mountedSnapdLibExecPath)() 423 424 // mock installed snap 425 snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{ 426 Revision: snap.R("x2"), 427 }) 428 429 restore := snaprun.MockOsReadlink(func(name string) (string, error) { 430 // pretend 'snap' is reexeced from 'core' 431 return filepath.Join(mountedSnapdPath, "usr/bin/snap"), nil 432 }) 433 defer restore() 434 435 execArgs := []string{} 436 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 437 execArgs = args 438 return nil 439 }) 440 defer restorer() 441 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 442 c.Assert(err, check.IsNil) 443 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 444 c.Check(execArgs, check.DeepEquals, []string{ 445 filepath.Join(mountedSnapdLibExecPath, "snap-confine"), "--classic", 446 "snap.snapname.app", 447 filepath.Join(mountedSnapdLibExecPath, "snap-exec"), 448 "snapname.app", "--arg1", "arg2"}) 449 } 450 451 func (s *RunSuite) TestSnapRunAppWithCommandIntegration(c *check.C) { 452 defer mockSnapConfine(dirs.DistroLibExecDir)() 453 454 // mock installed snap 455 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 456 Revision: snap.R(42), 457 }) 458 459 // redirect exec 460 execArg0 := "" 461 execArgs := []string{} 462 execEnv := []string{} 463 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 464 execArg0 = arg0 465 execArgs = args 466 execEnv = envv 467 return nil 468 }) 469 defer restorer() 470 471 // and run it! 472 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=my-command", "--", "snapname.app", "arg1", "arg2"}) 473 c.Assert(err, check.IsNil) 474 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 475 c.Check(execArgs, check.DeepEquals, []string{ 476 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 477 "snap.snapname.app", 478 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 479 "--command=my-command", "snapname.app", "arg1", "arg2"}) 480 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 481 } 482 483 func (s *RunSuite) TestSnapRunCreateDataDirs(c *check.C) { 484 for _, t := range []struct { 485 snapDir string 486 opts *dirs.SnapDirOptions 487 }{ 488 {snapDir: dirs.UserHomeSnapDir}, 489 {snapDir: dirs.UserHomeSnapDir, opts: &dirs.SnapDirOptions{}}, 490 {snapDir: dirs.HiddenSnapDataHomeDir, opts: &dirs.SnapDirOptions{HiddenSnapDataDir: true}}, 491 } { 492 s.testSnapRunCreateDataDirs(c, t.snapDir, t.opts) 493 c.Assert(os.RemoveAll(s.fakeHome), check.IsNil) 494 s.fakeHome = c.MkDir() 495 } 496 } 497 498 func (s *RunSuite) testSnapRunCreateDataDirs(c *check.C, snapDir string, opts *dirs.SnapDirOptions) { 499 info, err := snap.InfoFromSnapYaml(mockYaml) 500 c.Assert(err, check.IsNil) 501 info.SideInfo.Revision = snap.R(42) 502 503 err = snaprun.CreateUserDataDirs(info, opts) 504 c.Assert(err, check.IsNil) 505 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, snapDir, "snapname/42")), check.Equals, true) 506 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, snapDir, "snapname/common")), check.Equals, true) 507 508 // check we don't create the alternative dir 509 nonExistentDir := dirs.HiddenSnapDataHomeDir 510 if snapDir == dirs.HiddenSnapDataHomeDir { 511 nonExistentDir = dirs.UserHomeSnapDir 512 } 513 514 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, nonExistentDir)), check.Equals, false) 515 } 516 517 func (s *RunSuite) TestParallelInstanceSnapRunCreateDataDirs(c *check.C) { 518 info, err := snap.InfoFromSnapYaml(mockYaml) 519 c.Assert(err, check.IsNil) 520 info.SideInfo.Revision = snap.R(42) 521 info.InstanceKey = "foo" 522 523 err = snaprun.CreateUserDataDirs(info, nil) 524 c.Assert(err, check.IsNil) 525 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/42")), check.Equals, true) 526 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/common")), check.Equals, true) 527 // mount point for snap instance mapping has been created 528 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname")), check.Equals, true) 529 // and it's empty inside 530 m, err := filepath.Glob(filepath.Join(s.fakeHome, "/snap/snapname/*")) 531 c.Assert(err, check.IsNil) 532 c.Assert(m, check.HasLen, 0) 533 } 534 535 func (s *RunSuite) TestSnapRunHookIntegration(c *check.C) { 536 defer mockSnapConfine(dirs.DistroLibExecDir)() 537 538 // mock installed snap 539 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 540 Revision: snap.R(42), 541 }) 542 543 // redirect exec 544 execArg0 := "" 545 execArgs := []string{} 546 execEnv := []string{} 547 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 548 execArg0 = arg0 549 execArgs = args 550 execEnv = envv 551 return nil 552 }) 553 defer restorer() 554 555 // Run a hook from the active revision 556 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"}) 557 c.Assert(err, check.IsNil) 558 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 559 c.Check(execArgs, check.DeepEquals, []string{ 560 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 561 "snap.snapname.hook.configure", 562 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 563 "--hook=configure", "snapname"}) 564 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 565 } 566 567 func (s *RunSuite) TestSnapRunHookUnsetRevisionIntegration(c *check.C) { 568 defer mockSnapConfine(dirs.DistroLibExecDir)() 569 570 // mock installed snap 571 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 572 Revision: snap.R(42), 573 }) 574 575 // redirect exec 576 execArg0 := "" 577 execArgs := []string{} 578 execEnv := []string{} 579 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 580 execArg0 = arg0 581 execArgs = args 582 execEnv = envv 583 return nil 584 }) 585 defer restorer() 586 587 // Specifically pass "unset" which would use the active version. 588 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=unset", "--", "snapname"}) 589 c.Assert(err, check.IsNil) 590 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 591 c.Check(execArgs, check.DeepEquals, []string{ 592 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 593 "snap.snapname.hook.configure", 594 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 595 "--hook=configure", "snapname"}) 596 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 597 } 598 599 func (s *RunSuite) TestSnapRunHookSpecificRevisionIntegration(c *check.C) { 600 defer mockSnapConfine(dirs.DistroLibExecDir)() 601 602 // mock installed snap 603 // Create both revisions 41 and 42 604 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 605 Revision: snap.R(41), 606 }) 607 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 608 Revision: snap.R(42), 609 }) 610 611 // redirect exec 612 execArg0 := "" 613 execArgs := []string{} 614 execEnv := []string{} 615 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 616 execArg0 = arg0 617 execArgs = args 618 execEnv = envv 619 return nil 620 }) 621 defer restorer() 622 623 // Run a hook on revision 41 624 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=41", "--", "snapname"}) 625 c.Assert(err, check.IsNil) 626 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 627 c.Check(execArgs, check.DeepEquals, []string{ 628 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 629 "snap.snapname.hook.configure", 630 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 631 "--hook=configure", "snapname"}) 632 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=41") 633 } 634 635 func (s *RunSuite) TestSnapRunHookMissingRevisionIntegration(c *check.C) { 636 // Only create revision 42 637 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 638 Revision: snap.R(42), 639 }) 640 641 // redirect exec 642 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 643 return nil 644 }) 645 defer restorer() 646 647 // Attempt to run a hook on revision 41, which doesn't exist 648 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=41", "--", "snapname"}) 649 c.Assert(err, check.NotNil) 650 c.Check(err, check.ErrorMatches, "cannot find .*") 651 } 652 653 func (s *RunSuite) TestSnapRunHookInvalidRevisionIntegration(c *check.C) { 654 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=invalid", "--", "snapname"}) 655 c.Assert(err, check.NotNil) 656 c.Check(err, check.ErrorMatches, "invalid snap revision: \"invalid\"") 657 } 658 659 func (s *RunSuite) TestSnapRunHookMissingHookIntegration(c *check.C) { 660 // Only create revision 42 661 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 662 Revision: snap.R(42), 663 }) 664 665 // redirect exec 666 called := false 667 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 668 called = true 669 return nil 670 }) 671 defer restorer() 672 673 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=missing-hook", "--", "snapname"}) 674 c.Assert(err, check.ErrorMatches, `cannot find hook "missing-hook" in "snapname"`) 675 c.Check(called, check.Equals, false) 676 } 677 678 func (s *RunSuite) TestSnapRunErorsForUnknownRunArg(c *check.C) { 679 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--unknown", "--", "snapname.app", "--arg1", "arg2"}) 680 c.Assert(err, check.ErrorMatches, "unknown flag `unknown'") 681 } 682 683 func (s *RunSuite) TestSnapRunErorsForMissingApp(c *check.C) { 684 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=shell"}) 685 c.Assert(err, check.ErrorMatches, "need the application to run as argument") 686 } 687 688 func (s *RunSuite) TestSnapRunErorrForUnavailableApp(c *check.C) { 689 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "not-there"}) 690 c.Assert(err, check.ErrorMatches, fmt.Sprintf("cannot find current revision for snap not-there: readlink %s/not-there/current: no such file or directory", dirs.SnapMountDir)) 691 } 692 693 func (s *RunSuite) TestSnapRunSaneEnvironmentHandling(c *check.C) { 694 defer mockSnapConfine(dirs.DistroLibExecDir)() 695 696 // mock installed snap 697 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 698 Revision: snap.R(42), 699 }) 700 701 // redirect exec 702 execEnv := []string{} 703 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 704 execEnv = envv 705 return nil 706 }) 707 defer restorer() 708 709 // set a SNAP{,_*} variable in the environment 710 os.Setenv("SNAP_NAME", "something-else") 711 os.Setenv("SNAP_ARCH", "PDP-7") 712 defer os.Unsetenv("SNAP_NAME") 713 defer os.Unsetenv("SNAP_ARCH") 714 // but unrelated stuff is ok 715 os.Setenv("SNAP_THE_WORLD", "YES") 716 defer os.Unsetenv("SNAP_THE_WORLD") 717 718 // and ensure those SNAP_ vars get overridden 719 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 720 c.Assert(err, check.IsNil) 721 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 722 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 723 c.Check(execEnv, check.Not(testutil.Contains), "SNAP_NAME=something-else") 724 c.Check(execEnv, check.Not(testutil.Contains), "SNAP_ARCH=PDP-7") 725 c.Check(execEnv, testutil.Contains, "SNAP_THE_WORLD=YES") 726 } 727 728 func (s *RunSuite) TestSnapRunSnapdHelperPath(c *check.C) { 729 _, r := logger.MockLogger() 730 defer r() 731 732 var osReadlinkResult string 733 restore := snaprun.MockOsReadlink(func(name string) (string, error) { 734 return osReadlinkResult, nil 735 }) 736 defer restore() 737 738 tool := "snap-confine" 739 for _, t := range []struct { 740 readlink string 741 expected string 742 }{ 743 { 744 filepath.Join(dirs.SnapMountDir, "core/current/usr/bin/snap"), 745 filepath.Join(dirs.SnapMountDir, "core/current", dirs.CoreLibExecDir, tool), 746 }, 747 { 748 filepath.Join(dirs.SnapMountDir, "snapd/current/usr/bin/snap"), 749 filepath.Join(dirs.SnapMountDir, "snapd/current", dirs.CoreLibExecDir, tool), 750 }, 751 { 752 filepath.Join("/usr/bin/snap"), 753 filepath.Join(dirs.DistroLibExecDir, tool), 754 }, 755 { 756 filepath.Join("/home/foo/ws/snapd/snap"), 757 filepath.Join(dirs.DistroLibExecDir, tool), 758 }, 759 // unexpected case 760 { 761 filepath.Join(dirs.SnapMountDir, "snapd2/current/bin/snap"), 762 filepath.Join(dirs.DistroLibExecDir, tool), 763 }, 764 } { 765 osReadlinkResult = t.readlink 766 toolPath, err := snaprun.SnapdHelperPath(tool) 767 c.Assert(err, check.IsNil) 768 c.Check(toolPath, check.Equals, t.expected) 769 } 770 } 771 772 func (s *RunSuite) TestSnapRunAppIntegrationFromCore(c *check.C) { 773 defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))() 774 775 // mock installed snap 776 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 777 Revision: snap.R("x2"), 778 }) 779 780 // pretend to be running from core 781 restorer := snaprun.MockOsReadlink(func(string) (string, error) { 782 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 783 }) 784 defer restorer() 785 786 // redirect exec 787 execArg0 := "" 788 execArgs := []string{} 789 execEnv := []string{} 790 restorer = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 791 execArg0 = arg0 792 execArgs = args 793 execEnv = envv 794 return nil 795 }) 796 defer restorer() 797 798 // and run it! 799 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 800 c.Assert(err, check.IsNil) 801 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 802 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 803 c.Check(execArgs, check.DeepEquals, []string{ 804 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 805 "snap.snapname.app", 806 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 807 "snapname.app", "--arg1", "arg2"}) 808 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 809 } 810 811 func (s *RunSuite) TestSnapRunAppIntegrationFromSnapd(c *check.C) { 812 defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "snapd", "222", dirs.CoreLibExecDir))() 813 814 // mock installed snap 815 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 816 Revision: snap.R("x2"), 817 }) 818 819 // pretend to be running from snapd 820 restorer := snaprun.MockOsReadlink(func(string) (string, error) { 821 return filepath.Join(dirs.SnapMountDir, "snapd/222/usr/bin/snap"), nil 822 }) 823 defer restorer() 824 825 // redirect exec 826 execArg0 := "" 827 execArgs := []string{} 828 execEnv := []string{} 829 restorer = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 830 execArg0 = arg0 831 execArgs = args 832 execEnv = envv 833 return nil 834 }) 835 defer restorer() 836 837 // and run it! 838 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 839 c.Assert(err, check.IsNil) 840 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 841 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/snapd/222", dirs.CoreLibExecDir, "snap-confine")) 842 c.Check(execArgs, check.DeepEquals, []string{ 843 filepath.Join(dirs.SnapMountDir, "/snapd/222", dirs.CoreLibExecDir, "snap-confine"), 844 "snap.snapname.app", 845 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 846 "snapname.app", "--arg1", "arg2"}) 847 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 848 } 849 850 func (s *RunSuite) TestSnapRunXauthorityMigration(c *check.C) { 851 defer mockSnapConfine(dirs.DistroLibExecDir)() 852 853 u, err := user.Current() 854 c.Assert(err, check.IsNil) 855 856 // Ensure XDG_RUNTIME_DIR exists for the user we're testing with 857 err = os.MkdirAll(filepath.Join(dirs.XdgRuntimeDirBase, u.Uid), 0700) 858 c.Assert(err, check.IsNil) 859 860 // mock installed snap; happily this also gives us a directory 861 // below /tmp which the Xauthority migration expects. 862 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 863 Revision: snap.R("x2"), 864 }) 865 866 // redirect exec 867 execArg0 := "" 868 execArgs := []string{} 869 execEnv := []string{} 870 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 871 execArg0 = arg0 872 execArgs = args 873 execEnv = envv 874 return nil 875 }) 876 defer restorer() 877 878 xauthPath, err := x11.MockXauthority(2) 879 c.Assert(err, check.IsNil) 880 defer os.Remove(xauthPath) 881 882 defer snaprun.MockGetEnv(func(name string) string { 883 if name == "XAUTHORITY" { 884 return xauthPath 885 } 886 return "" 887 })() 888 889 // and run it! 890 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 891 c.Assert(err, check.IsNil) 892 c.Assert(rest, check.DeepEquals, []string{"snapname.app"}) 893 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 894 c.Check(execArgs, check.DeepEquals, []string{ 895 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 896 "snap.snapname.app", 897 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 898 "snapname.app"}) 899 900 expectedXauthPath := filepath.Join(dirs.XdgRuntimeDirBase, u.Uid, ".Xauthority") 901 c.Check(execEnv, testutil.Contains, fmt.Sprintf("XAUTHORITY=%s", expectedXauthPath)) 902 903 info, err := os.Stat(expectedXauthPath) 904 c.Assert(err, check.IsNil) 905 c.Assert(info.Mode().Perm(), check.Equals, os.FileMode(0600)) 906 907 err = x11.ValidateXauthorityFile(expectedXauthPath) 908 c.Assert(err, check.IsNil) 909 } 910 911 // build the args for a hypothetical completer 912 func mkCompArgs(compPoint string, argv ...string) []string { 913 out := []string{ 914 "99", // COMP_TYPE 915 "99", // COMP_KEY 916 "", // COMP_POINT 917 "2", // COMP_CWORD 918 " ", // COMP_WORDBREAKS 919 } 920 out[2] = compPoint 921 out = append(out, strings.Join(argv, " ")) 922 out = append(out, argv...) 923 return out 924 } 925 926 func (s *RunSuite) TestAntialiasHappy(c *check.C) { 927 c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil) 928 929 inArgs := mkCompArgs("10", "alias", "alias", "bo-alias") 930 931 // first not so happy because no alias symlink 932 app, outArgs := snaprun.Antialias("alias", inArgs) 933 c.Check(app, check.Equals, "alias") 934 c.Check(outArgs, check.DeepEquals, inArgs) 935 936 c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil) 937 938 // now really happy 939 app, outArgs = snaprun.Antialias("alias", inArgs) 940 c.Check(app, check.Equals, "an-app") 941 c.Check(outArgs, check.DeepEquals, []string{ 942 "99", // COMP_TYPE (no change) 943 "99", // COMP_KEY (no change) 944 "11", // COMP_POINT (+1 because "an-app" is one longer than "alias") 945 "2", // COMP_CWORD (no change) 946 " ", // COMP_WORDBREAKS (no change) 947 "an-app alias bo-alias", // COMP_LINE (argv[0] changed) 948 "an-app", // argv (arv[0] changed) 949 "alias", 950 "bo-alias", 951 }) 952 } 953 954 func (s *RunSuite) TestAntialiasBailsIfUnhappy(c *check.C) { 955 // alias exists but args are somehow wonky 956 c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil) 957 c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil) 958 959 // weird1 has COMP_LINE not start with COMP_WORDS[0], argv[0] equal to COMP_WORDS[0] 960 weird1 := mkCompArgs("6", "alias", "") 961 weird1[5] = "xxxxx " 962 // weird2 has COMP_LINE not start with COMP_WORDS[0], argv[0] equal to the first word in COMP_LINE 963 weird2 := mkCompArgs("6", "xxxxx", "") 964 weird2[5] = "alias " 965 966 for desc, inArgs := range map[string][]string{ 967 "nil args": nil, 968 "too-short args": {"alias"}, 969 "COMP_POINT not a number": mkCompArgs("hello", "alias"), 970 "COMP_POINT is inside argv[0]": mkCompArgs("2", "alias", ""), 971 "COMP_POINT is outside argv": mkCompArgs("99", "alias", ""), 972 "COMP_WORDS[0] is not argv[0]": mkCompArgs("10", "not-alias", ""), 973 "mismatch between argv[0], COMP_LINE and COMP_WORDS, #1": weird1, 974 "mismatch between argv[0], COMP_LINE and COMP_WORDS, #2": weird2, 975 } { 976 // antialias leaves args alone if it's too short 977 app, outArgs := snaprun.Antialias("alias", inArgs) 978 c.Check(app, check.Equals, "alias", check.Commentf(desc)) 979 c.Check(outArgs, check.DeepEquals, inArgs, check.Commentf(desc)) 980 } 981 } 982 983 func (s *RunSuite) TestSnapRunAppWithStraceIntegration(c *check.C) { 984 defer mockSnapConfine(dirs.DistroLibExecDir)() 985 986 // mock installed snap 987 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 988 Revision: snap.R("x2"), 989 }) 990 991 // pretend we have sudo and simulate some useful output that would 992 // normally come from strace 993 sudoCmd := testutil.MockCommand(c, "sudo", fmt.Sprintf(` 994 echo "stdout output 1" 995 >&2 echo 'execve("/path/to/snap-confine")' 996 >&2 echo "snap-confine/snap-exec strace stuff" 997 >&2 echo "getuid() = 1000" 998 >&2 echo 'execve("%s/snapName/x2/bin/foo")' 999 >&2 echo "interessting strace output" 1000 >&2 echo "and more" 1001 echo "stdout output 2" 1002 `, dirs.SnapMountDir)) 1003 defer sudoCmd.Restore() 1004 1005 // pretend we have strace 1006 straceCmd := testutil.MockCommand(c, "strace", "") 1007 defer straceCmd.Restore() 1008 1009 user, err := user.Current() 1010 c.Assert(err, check.IsNil) 1011 1012 // and run it under strace 1013 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--strace", "--", "snapname.app", "--arg1", "arg2"}) 1014 c.Assert(err, check.IsNil) 1015 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1016 c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{ 1017 { 1018 "sudo", "-E", 1019 filepath.Join(straceCmd.BinDir(), "strace"), 1020 "-u", user.Username, 1021 "-f", 1022 "-e", strace.ExcludedSyscalls, 1023 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 1024 "snap.snapname.app", 1025 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1026 "snapname.app", "--arg1", "arg2", 1027 }, 1028 }) 1029 c.Check(s.Stdout(), check.Equals, "stdout output 1\nstdout output 2\n") 1030 c.Check(s.Stderr(), check.Equals, fmt.Sprintf("execve(%q)\ninteressting strace output\nand more\n", filepath.Join(dirs.SnapMountDir, "snapName/x2/bin/foo"))) 1031 1032 s.ResetStdStreams() 1033 sudoCmd.ForgetCalls() 1034 1035 // try again without filtering 1036 rest, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--strace=--raw", "--", "snapname.app", "--arg1", "arg2"}) 1037 c.Assert(err, check.IsNil) 1038 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1039 c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{ 1040 { 1041 "sudo", "-E", 1042 filepath.Join(straceCmd.BinDir(), "strace"), 1043 "-u", user.Username, 1044 "-f", 1045 "-e", strace.ExcludedSyscalls, 1046 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 1047 "snap.snapname.app", 1048 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1049 "snapname.app", "--arg1", "arg2", 1050 }, 1051 }) 1052 c.Check(s.Stdout(), check.Equals, "stdout output 1\nstdout output 2\n") 1053 expectedFullFmt := `execve("/path/to/snap-confine") 1054 snap-confine/snap-exec strace stuff 1055 getuid() = 1000 1056 execve("%s/snapName/x2/bin/foo") 1057 interessting strace output 1058 and more 1059 ` 1060 c.Check(s.Stderr(), check.Equals, fmt.Sprintf(expectedFullFmt, dirs.SnapMountDir)) 1061 } 1062 1063 func (s *RunSuite) TestSnapRunAppWithStraceOptions(c *check.C) { 1064 defer mockSnapConfine(dirs.DistroLibExecDir)() 1065 1066 // mock installed snap 1067 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1068 Revision: snap.R("x2"), 1069 }) 1070 1071 // pretend we have sudo 1072 sudoCmd := testutil.MockCommand(c, "sudo", "") 1073 defer sudoCmd.Restore() 1074 1075 // pretend we have strace 1076 straceCmd := testutil.MockCommand(c, "strace", "") 1077 defer straceCmd.Restore() 1078 1079 user, err := user.Current() 1080 c.Assert(err, check.IsNil) 1081 1082 // and run it under strace 1083 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--strace=-tt --raw -o "file with spaces"`, "--", "snapname.app", "--arg1", "arg2"}) 1084 c.Assert(err, check.IsNil) 1085 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1086 c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{ 1087 { 1088 "sudo", "-E", 1089 filepath.Join(straceCmd.BinDir(), "strace"), 1090 "-u", user.Username, 1091 "-f", 1092 "-e", strace.ExcludedSyscalls, 1093 "-tt", 1094 "-o", 1095 "file with spaces", 1096 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 1097 "snap.snapname.app", 1098 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1099 "snapname.app", "--arg1", "arg2", 1100 }, 1101 }) 1102 } 1103 1104 func (s *RunSuite) TestSnapRunShellIntegration(c *check.C) { 1105 defer mockSnapConfine(dirs.DistroLibExecDir)() 1106 1107 // mock installed snap 1108 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1109 Revision: snap.R("x2"), 1110 }) 1111 1112 // redirect exec 1113 execArg0 := "" 1114 execArgs := []string{} 1115 execEnv := []string{} 1116 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1117 execArg0 = arg0 1118 execArgs = args 1119 execEnv = envv 1120 return nil 1121 }) 1122 defer restorer() 1123 1124 // and run it! 1125 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--shell", "--", "snapname.app", "--arg1", "arg2"}) 1126 c.Assert(err, check.IsNil) 1127 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1128 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 1129 c.Check(execArgs, check.DeepEquals, []string{ 1130 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 1131 "snap.snapname.app", 1132 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1133 "--command=shell", "snapname.app", "--arg1", "arg2"}) 1134 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1135 } 1136 1137 func (s *RunSuite) TestSnapRunAppTimer(c *check.C) { 1138 defer mockSnapConfine(dirs.DistroLibExecDir)() 1139 1140 // mock installed snap 1141 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1142 Revision: snap.R("x2"), 1143 }) 1144 1145 // redirect exec 1146 execArg0 := "" 1147 execArgs := []string{} 1148 execCalled := false 1149 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1150 execArg0 = arg0 1151 execArgs = args 1152 execCalled = true 1153 return nil 1154 }) 1155 defer restorer() 1156 1157 fakeNow := time.Date(2018, 02, 12, 9, 55, 0, 0, time.Local) 1158 restorer = snaprun.MockTimeNow(func() time.Time { 1159 // Monday Feb 12, 9:55 1160 return fakeNow 1161 }) 1162 defer restorer() 1163 1164 // pretend we are outside of timer range 1165 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--timer="mon,10:00~12:00,,fri,13:00"`, "--", "snapname.app", "--arg1", "arg2"}) 1166 c.Assert(err, check.IsNil) 1167 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1168 c.Assert(execCalled, check.Equals, false) 1169 1170 c.Check(s.Stderr(), check.Equals, fmt.Sprintf(`%s: attempted to run "snapname.app" timer outside of scheduled time "mon,10:00~12:00,,fri,13:00" 1171 `, fakeNow.Format(time.RFC3339))) 1172 s.ResetStdStreams() 1173 1174 restorer = snaprun.MockTimeNow(func() time.Time { 1175 // Monday Feb 12, 10:20 1176 return time.Date(2018, 02, 12, 10, 20, 0, 0, time.Local) 1177 }) 1178 defer restorer() 1179 1180 // and run it under strace 1181 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--timer="mon,10:00~12:00,,fri,13:00"`, "--", "snapname.app", "--arg1", "arg2"}) 1182 c.Assert(err, check.IsNil) 1183 c.Assert(execCalled, check.Equals, true) 1184 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 1185 c.Check(execArgs, check.DeepEquals, []string{ 1186 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 1187 "snap.snapname.app", 1188 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1189 "snapname.app", "--arg1", "arg2"}) 1190 } 1191 1192 func (s *RunSuite) TestRunCmdWithTraceExecUnhappy(c *check.C) { 1193 _, r := logger.MockLogger() 1194 defer r() 1195 1196 defer mockSnapConfine(dirs.DistroLibExecDir)() 1197 1198 // mock installed snap 1199 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1200 Revision: snap.R("1"), 1201 }) 1202 1203 // pretend we have sudo 1204 sudoCmd := testutil.MockCommand(c, "sudo", "echo unhappy; exit 12") 1205 defer sudoCmd.Restore() 1206 1207 // pretend we have strace 1208 straceCmd := testutil.MockCommand(c, "strace", "") 1209 defer straceCmd.Restore() 1210 1211 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--trace-exec", "--", "snapname.app", "--arg1", "arg2"}) 1212 c.Assert(err, check.ErrorMatches, "exit status 12") 1213 c.Assert(rest, check.DeepEquals, []string{"--", "snapname.app", "--arg1", "arg2"}) 1214 c.Check(s.Stdout(), check.Equals, "unhappy\n") 1215 c.Check(s.Stderr(), check.Equals, "") 1216 } 1217 1218 func (s *RunSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) { 1219 logbuf, restorer := logger.MockLogger() 1220 defer restorer() 1221 1222 defer mockSnapConfine(dirs.DistroLibExecDir)() 1223 1224 // mock installed snap 1225 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1226 Revision: snap.R("x2"), 1227 }) 1228 1229 // redirect exec 1230 execCalled := 0 1231 restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error { 1232 execCalled++ 1233 return nil 1234 }) 1235 defer restorer() 1236 1237 verifyCalls := 0 1238 restoreCalls := 0 1239 isEnabledCalls := 0 1240 enabled := false 1241 verify := true 1242 1243 snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir) 1244 1245 restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) { 1246 c.Check(what, check.Equals, snapUserDir) 1247 verifyCalls++ 1248 return verify, nil 1249 }) 1250 defer restorer() 1251 1252 restorer = snaprun.MockSELinuxRestoreContext(func(what string, mode selinux.RestoreMode) error { 1253 c.Check(mode, check.Equals, selinux.RestoreMode{Recursive: true}) 1254 c.Check(what, check.Equals, snapUserDir) 1255 restoreCalls++ 1256 return nil 1257 }) 1258 defer restorer() 1259 1260 restorer = snaprun.MockSELinuxIsEnabled(func() (bool, error) { 1261 isEnabledCalls++ 1262 return enabled, nil 1263 }) 1264 defer restorer() 1265 1266 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1267 c.Assert(err, check.IsNil) 1268 c.Check(execCalled, check.Equals, 1) 1269 c.Check(isEnabledCalls, check.Equals, 1) 1270 c.Check(verifyCalls, check.Equals, 0) 1271 c.Check(restoreCalls, check.Equals, 0) 1272 1273 // pretend SELinux is on 1274 enabled = true 1275 1276 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1277 c.Assert(err, check.IsNil) 1278 c.Check(execCalled, check.Equals, 2) 1279 c.Check(isEnabledCalls, check.Equals, 2) 1280 c.Check(verifyCalls, check.Equals, 1) 1281 c.Check(restoreCalls, check.Equals, 0) 1282 1283 // pretend the context does not match 1284 verify = false 1285 1286 logbuf.Reset() 1287 1288 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1289 c.Assert(err, check.IsNil) 1290 c.Check(execCalled, check.Equals, 3) 1291 c.Check(isEnabledCalls, check.Equals, 3) 1292 c.Check(verifyCalls, check.Equals, 2) 1293 c.Check(restoreCalls, check.Equals, 1) 1294 1295 // and we let the user know what we're doing 1296 c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("restoring default SELinux context of %s", snapUserDir)) 1297 } 1298 1299 func (s *RunSuite) TestSnapRunRestoreSecurityContextFail(c *check.C) { 1300 logbuf, restorer := logger.MockLogger() 1301 defer restorer() 1302 1303 defer mockSnapConfine(dirs.DistroLibExecDir)() 1304 1305 // mock installed snap 1306 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1307 Revision: snap.R("x2"), 1308 }) 1309 1310 // redirect exec 1311 execCalled := 0 1312 restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error { 1313 execCalled++ 1314 return nil 1315 }) 1316 defer restorer() 1317 1318 verifyCalls := 0 1319 restoreCalls := 0 1320 isEnabledCalls := 0 1321 enabledErr := errors.New("enabled failed") 1322 verifyErr := errors.New("verify failed") 1323 restoreErr := errors.New("restore failed") 1324 1325 snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir) 1326 1327 restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) { 1328 c.Check(what, check.Equals, snapUserDir) 1329 verifyCalls++ 1330 return false, verifyErr 1331 }) 1332 defer restorer() 1333 1334 restorer = snaprun.MockSELinuxRestoreContext(func(what string, mode selinux.RestoreMode) error { 1335 c.Check(mode, check.Equals, selinux.RestoreMode{Recursive: true}) 1336 c.Check(what, check.Equals, snapUserDir) 1337 restoreCalls++ 1338 return restoreErr 1339 }) 1340 defer restorer() 1341 1342 restorer = snaprun.MockSELinuxIsEnabled(func() (bool, error) { 1343 isEnabledCalls++ 1344 return enabledErr == nil, enabledErr 1345 }) 1346 defer restorer() 1347 1348 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1349 // these errors are only logged, but we still run the snap 1350 c.Assert(err, check.IsNil) 1351 c.Check(execCalled, check.Equals, 1) 1352 c.Check(logbuf.String(), testutil.Contains, "cannot determine SELinux status: enabled failed") 1353 c.Check(isEnabledCalls, check.Equals, 1) 1354 c.Check(verifyCalls, check.Equals, 0) 1355 c.Check(restoreCalls, check.Equals, 0) 1356 // pretend selinux is on 1357 enabledErr = nil 1358 1359 logbuf.Reset() 1360 1361 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1362 c.Assert(err, check.IsNil) 1363 c.Check(execCalled, check.Equals, 2) 1364 c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("failed to verify SELinux context of %s: verify failed", snapUserDir)) 1365 c.Check(isEnabledCalls, check.Equals, 2) 1366 c.Check(verifyCalls, check.Equals, 1) 1367 c.Check(restoreCalls, check.Equals, 0) 1368 1369 // pretend the context does not match 1370 verifyErr = nil 1371 1372 logbuf.Reset() 1373 1374 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1375 c.Assert(err, check.IsNil) 1376 c.Check(execCalled, check.Equals, 3) 1377 c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("cannot restore SELinux context of %s: restore failed", snapUserDir)) 1378 c.Check(isEnabledCalls, check.Equals, 3) 1379 c.Check(verifyCalls, check.Equals, 2) 1380 c.Check(restoreCalls, check.Equals, 1) 1381 } 1382 1383 // systemctl is-system-running returns "running" in normal situations. 1384 func (s *RunSuite) TestIsStoppingRunning(c *check.C) { 1385 systemctl := testutil.MockCommand(c, "systemctl", ` 1386 case "$1" in 1387 is-system-running) 1388 echo "running" 1389 exit 0 1390 ;; 1391 esac 1392 `) 1393 defer systemctl.Restore() 1394 stop, err := snaprun.IsStopping() 1395 c.Check(err, check.IsNil) 1396 c.Check(stop, check.Equals, false) 1397 c.Check(systemctl.Calls(), check.DeepEquals, [][]string{ 1398 {"systemctl", "is-system-running"}, 1399 }) 1400 } 1401 1402 // systemctl is-system-running returns "stopping" when the system is 1403 // shutting down or rebooting. At the same time it returns a non-zero 1404 // exit status. 1405 func (s *RunSuite) TestIsStoppingStopping(c *check.C) { 1406 systemctl := testutil.MockCommand(c, "systemctl", ` 1407 case "$1" in 1408 is-system-running) 1409 echo "stopping" 1410 exit 1 1411 ;; 1412 esac 1413 `) 1414 defer systemctl.Restore() 1415 stop, err := snaprun.IsStopping() 1416 c.Check(err, check.IsNil) 1417 c.Check(stop, check.Equals, true) 1418 c.Check(systemctl.Calls(), check.DeepEquals, [][]string{ 1419 {"systemctl", "is-system-running"}, 1420 }) 1421 } 1422 1423 // systemctl is-system-running can often return "degraded" 1424 // Let's make sure that is not confusing us. 1425 func (s *RunSuite) TestIsStoppingDegraded(c *check.C) { 1426 systemctl := testutil.MockCommand(c, "systemctl", ` 1427 case "$1" in 1428 is-system-running) 1429 echo "degraded" 1430 exit 1 1431 ;; 1432 esac 1433 `) 1434 defer systemctl.Restore() 1435 stop, err := snaprun.IsStopping() 1436 c.Check(err, check.IsNil) 1437 c.Check(stop, check.Equals, false) 1438 c.Check(systemctl.Calls(), check.DeepEquals, [][]string{ 1439 {"systemctl", "is-system-running"}, 1440 }) 1441 } 1442 1443 func (s *RunSuite) TestSnapRunTrackingApps(c *check.C) { 1444 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1445 defer restore() 1446 1447 // mock installed snap 1448 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1449 Revision: snap.R("x2"), 1450 }) 1451 1452 // pretend to be running from core 1453 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1454 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1455 }) 1456 defer restore() 1457 1458 created := false 1459 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1460 c.Assert(securityTag, check.Equals, "snap.snapname.app") 1461 c.Assert(opts, check.NotNil) 1462 c.Assert(opts.AllowSessionBus, check.Equals, true) 1463 created = true 1464 return nil 1465 }) 1466 defer restore() 1467 1468 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1469 panic("apps need to create a scope and do not use systemd service tracking") 1470 }) 1471 defer restore() 1472 1473 // redirect exec 1474 execArg0 := "" 1475 execArgs := []string{} 1476 execEnv := []string{} 1477 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1478 execArg0 = arg0 1479 execArgs = args 1480 execEnv = envv 1481 return nil 1482 }) 1483 defer restore() 1484 1485 // and run it! 1486 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 1487 c.Assert(err, check.IsNil) 1488 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1489 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1490 c.Check(execArgs, check.DeepEquals, []string{ 1491 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1492 "snap.snapname.app", 1493 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1494 "snapname.app", "--arg1", "arg2"}) 1495 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1496 c.Assert(created, check.Equals, true) 1497 } 1498 1499 func (s *RunSuite) TestSnapRunTrackingHooks(c *check.C) { 1500 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1501 defer restore() 1502 1503 // mock installed snap 1504 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1505 Revision: snap.R("x2"), 1506 }) 1507 1508 // pretend to be running from core 1509 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1510 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1511 }) 1512 defer restore() 1513 1514 created := false 1515 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1516 c.Assert(securityTag, check.Equals, "snap.snapname.hook.configure") 1517 c.Assert(opts, check.NotNil) 1518 c.Assert(opts.AllowSessionBus, check.Equals, false) 1519 created = true 1520 return nil 1521 }) 1522 defer restore() 1523 1524 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1525 panic("hooks need to create a scope and do not use systemd service tracking") 1526 }) 1527 defer restore() 1528 1529 // redirect exec 1530 execArg0 := "" 1531 execArgs := []string{} 1532 execEnv := []string{} 1533 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1534 execArg0 = arg0 1535 execArgs = args 1536 execEnv = envv 1537 return nil 1538 }) 1539 defer restore() 1540 1541 // and run it! 1542 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook", "configure", "-r", "x2", "snapname"}) 1543 c.Assert(err, check.IsNil) 1544 c.Assert(rest, check.DeepEquals, []string{"snapname"}) 1545 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1546 c.Check(execArgs, check.DeepEquals, []string{ 1547 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1548 "snap.snapname.hook.configure", 1549 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1550 "--hook=configure", "snapname"}) 1551 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1552 c.Assert(created, check.Equals, true) 1553 } 1554 1555 func (s *RunSuite) TestSnapRunTrackingServices(c *check.C) { 1556 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1557 defer restore() 1558 1559 // mock installed snap 1560 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1561 Revision: snap.R("x2"), 1562 }) 1563 1564 // pretend to be running from core 1565 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1566 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1567 }) 1568 defer restore() 1569 1570 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1571 panic("services rely on systemd tracking, should not have created a transient scope") 1572 }) 1573 defer restore() 1574 1575 confirmed := false 1576 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1577 confirmed = true 1578 c.Assert(securityTag, check.Equals, "snap.snapname.svc") 1579 return nil 1580 }) 1581 defer restore() 1582 1583 // redirect exec 1584 execArg0 := "" 1585 execArgs := []string{} 1586 execEnv := []string{} 1587 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1588 execArg0 = arg0 1589 execArgs = args 1590 execEnv = envv 1591 return nil 1592 }) 1593 defer restore() 1594 1595 // and run it! 1596 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.svc", "--arg1", "arg2"}) 1597 c.Assert(err, check.IsNil) 1598 c.Assert(rest, check.DeepEquals, []string{"snapname.svc", "--arg1", "arg2"}) 1599 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1600 c.Check(execArgs, check.DeepEquals, []string{ 1601 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1602 "snap.snapname.svc", 1603 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1604 "snapname.svc", "--arg1", "arg2"}) 1605 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1606 c.Assert(confirmed, check.Equals, true) 1607 } 1608 1609 func (s *RunSuite) TestSnapRunTrackingServicesWhenRunByUser(c *check.C) { 1610 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1611 defer restore() 1612 1613 // mock installed snap 1614 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1615 Revision: snap.R("x2"), 1616 }) 1617 1618 // pretend to be running from core 1619 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1620 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1621 }) 1622 defer restore() 1623 1624 var createTransientScopeOpts *cgroup.TrackingOptions 1625 var createTransientScopeCalls int 1626 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1627 createTransientScopeCalls++ 1628 createTransientScopeOpts = opts 1629 return nil 1630 }) 1631 defer restore() 1632 1633 confirmCalls := 0 1634 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1635 confirmCalls++ 1636 c.Assert(securityTag, check.Equals, "snap.snapname.svc") 1637 return cgroup.ErrCannotTrackProcess 1638 }) 1639 defer restore() 1640 1641 // redirect exec 1642 execArg0 := "" 1643 execArgs := []string{} 1644 execEnv := []string{} 1645 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1646 execArg0 = arg0 1647 execArgs = args 1648 execEnv = envv 1649 return nil 1650 }) 1651 defer restore() 1652 1653 // invoked as: snap run -- snapname.svc --arg1 arg2 1654 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.svc", "--arg1", "arg2"}) 1655 c.Assert(err, check.IsNil) 1656 c.Assert(rest, check.DeepEquals, []string{"snapname.svc", "--arg1", "arg2"}) 1657 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1658 c.Check(execArgs, check.DeepEquals, []string{ 1659 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1660 "snap.snapname.svc", 1661 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1662 "snapname.svc", "--arg1", "arg2"}) 1663 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1664 c.Assert(confirmCalls, check.Equals, 1) 1665 c.Assert(createTransientScopeCalls, check.Equals, 1) 1666 c.Assert(createTransientScopeOpts, check.DeepEquals, &cgroup.TrackingOptions{ 1667 AllowSessionBus: true, 1668 }) 1669 } 1670 1671 func (s *RunSuite) TestSnapRunTrackingFailure(c *check.C) { 1672 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1673 defer restore() 1674 1675 // mock installed snap 1676 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1677 Revision: snap.R("x2"), 1678 }) 1679 1680 // pretend to be running from core 1681 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1682 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1683 }) 1684 defer restore() 1685 1686 created := false 1687 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1688 c.Assert(securityTag, check.Equals, "snap.snapname.app") 1689 c.Assert(opts, check.NotNil) 1690 c.Assert(opts.AllowSessionBus, check.Equals, true) 1691 created = true 1692 // Pretend that the tracking system was unable to track this application. 1693 return cgroup.ErrCannotTrackProcess 1694 }) 1695 defer restore() 1696 1697 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1698 panic("apps need to create a scope and do not use systemd service tracking") 1699 }) 1700 defer restore() 1701 1702 // redirect exec 1703 execArg0 := "" 1704 execArgs := []string{} 1705 execEnv := []string{} 1706 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1707 execArg0 = arg0 1708 execArgs = args 1709 execEnv = envv 1710 return nil 1711 }) 1712 defer restore() 1713 1714 // Capture the debug log that is printed by this test. 1715 os.Setenv("SNAPD_DEBUG", "1") 1716 defer os.Unsetenv("SNAPD_DEBUG") 1717 logbuf, restore := logger.MockLogger() 1718 defer restore() 1719 1720 // and run it! 1721 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 1722 c.Assert(err, check.IsNil) 1723 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1724 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1725 c.Check(execArgs, check.DeepEquals, []string{ 1726 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1727 "snap.snapname.app", 1728 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1729 "snapname.app", "--arg1", "arg2"}) 1730 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1731 c.Assert(created, check.Equals, true) 1732 1733 // Ensure that the debug message is printed. 1734 c.Assert(logbuf.String(), testutil.Contains, "snapd cannot track the started application\n") 1735 } 1736 1737 var mockKernelYaml = []byte(`name: pc-kernel 1738 type: kernel 1739 version: 1.0 1740 hooks: 1741 fde-setup: 1742 `) 1743 1744 func (s *RunSuite) TestSnapRunHookKernelImplicitBase(c *check.C) { 1745 defer mockSnapConfine(dirs.DistroLibExecDir)() 1746 1747 nModel := 0 1748 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1749 switch r.URL.Path { 1750 case "/v2/model": 1751 switch nModel { 1752 case 0: 1753 c.Check(r.Method, check.Equals, "GET") 1754 c.Check(r.URL.RawQuery, check.Equals, "") 1755 fmt.Fprint(w, happyUC20ModelAssertionResponse) 1756 default: 1757 c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModel+1) 1758 } 1759 nModel++ 1760 } 1761 }) 1762 1763 // mock installed kernel 1764 snaptest.MockSnapCurrent(c, string(mockKernelYaml), &snap.SideInfo{ 1765 Revision: snap.R(42), 1766 }) 1767 1768 // redirect exec 1769 execArg0 := "" 1770 execArgs := []string{} 1771 execEnv := []string{} 1772 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1773 execArg0 = arg0 1774 execArgs = args 1775 execEnv = envv 1776 return nil 1777 }) 1778 defer restorer() 1779 1780 // Run a hook from the active revision 1781 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=fde-setup", "--", "pc-kernel"}) 1782 c.Assert(err, check.IsNil) 1783 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 1784 c.Check(execArgs, check.DeepEquals, []string{ 1785 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 1786 "--base", "core20", 1787 "snap.pc-kernel.hook.fde-setup", 1788 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1789 "--hook=fde-setup", "pc-kernel"}) 1790 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 1791 c.Check(nModel, check.Equals, 1) 1792 } 1793 1794 func (s *RunSuite) TestRunGdbserverNoGdbserver(c *check.C) { 1795 oldPath := os.Getenv("PATH") 1796 os.Setenv("PATH", "/no-path:/really-not") 1797 defer os.Setenv("PATH", oldPath) 1798 1799 defer mockSnapConfine(dirs.DistroLibExecDir)() 1800 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1801 Revision: snap.R("x2"), 1802 }) 1803 1804 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--gdbserver", "snapname.app"}) 1805 c.Assert(err, check.ErrorMatches, "please install gdbserver on your system") 1806 } 1807 1808 func (s *RunSuite) TestWaitInhibitUnlock(c *check.C) { 1809 var called int 1810 restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) { 1811 called++ 1812 if called < 5 { 1813 return runinhibit.HintInhibitedForRefresh, nil 1814 } 1815 return runinhibit.HintNotInhibited, nil 1816 }) 1817 defer restore() 1818 1819 notInhibited, err := snaprun.WaitInhibitUnlock("some-snap", runinhibit.HintNotInhibited) 1820 c.Assert(err, check.IsNil) 1821 c.Check(notInhibited, check.Equals, true) 1822 c.Check(called, check.Equals, 5) 1823 } 1824 1825 func (s *RunSuite) TestWaitInhibitUnlockWaitsForSpecificHint(c *check.C) { 1826 var called int 1827 restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) { 1828 called++ 1829 if called < 5 { 1830 return runinhibit.HintInhibitedGateRefresh, nil 1831 } 1832 return runinhibit.HintInhibitedForRefresh, nil 1833 }) 1834 defer restore() 1835 1836 notInhibited, err := snaprun.WaitInhibitUnlock("some-snap", runinhibit.HintInhibitedForRefresh) 1837 c.Assert(err, check.IsNil) 1838 c.Check(notInhibited, check.Equals, false) 1839 c.Check(called, check.Equals, 5) 1840 } 1841 1842 func (s *RunSuite) TestWaitWhileInhibitedNoop(c *check.C) { 1843 var called int 1844 restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) { 1845 called++ 1846 if called < 2 { 1847 return runinhibit.HintInhibitedGateRefresh, nil 1848 } 1849 return runinhibit.HintNotInhibited, nil 1850 }) 1851 defer restore() 1852 1853 meter := &progresstest.Meter{} 1854 defer progress.MockMeter(meter)() 1855 1856 c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedGateRefresh), check.IsNil) 1857 c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.IsNil) 1858 c.Check(called, check.Equals, 2) 1859 1860 c.Check(meter.Values, check.HasLen, 0) 1861 c.Check(meter.Written, check.HasLen, 0) 1862 c.Check(meter.Finishes, check.Equals, 0) 1863 c.Check(meter.Labels, check.HasLen, 0) 1864 c.Check(meter.Labels, check.HasLen, 0) 1865 } 1866 1867 func (s *RunSuite) TestWaitWhileInhibitedTextFlow(c *check.C) { 1868 var called int 1869 restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) { 1870 called++ 1871 if called < 2 { 1872 return runinhibit.HintInhibitedForRefresh, nil 1873 } 1874 return runinhibit.HintNotInhibited, nil 1875 }) 1876 defer restore() 1877 1878 meter := &progresstest.Meter{} 1879 defer progress.MockMeter(meter)() 1880 1881 c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedGateRefresh), check.IsNil) 1882 c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.IsNil) 1883 c.Check(called, check.Equals, 2) 1884 1885 c.Check(s.Stdout(), check.Equals, "snap package cannot be used now: gate-refresh\n") 1886 c.Check(meter.Values, check.HasLen, 0) 1887 c.Check(meter.Written, check.HasLen, 0) 1888 c.Check(meter.Finishes, check.Equals, 1) 1889 c.Check(meter.Labels, check.DeepEquals, []string{"please wait..."}) 1890 } 1891 1892 func (s *RunSuite) TestWaitWhileInhibitedGraphicalSessionFlow(c *check.C) { 1893 restoreIsGraphicalSession := snaprun.MockIsGraphicalSession(true) 1894 defer restoreIsGraphicalSession() 1895 1896 var notification *usersessionclient.PendingSnapRefreshInfo 1897 restorePendingRefreshNotification := snaprun.MockPendingRefreshNotification(func(refreshInfo *usersessionclient.PendingSnapRefreshInfo) error { 1898 notification = refreshInfo 1899 return nil 1900 }) 1901 defer restorePendingRefreshNotification() 1902 1903 var finishNotification *usersessionclient.FinishedSnapRefreshInfo 1904 restoreFinishRefreshNotification := snaprun.MockFinishRefreshNotification(func(refreshInfo *usersessionclient.FinishedSnapRefreshInfo) error { 1905 finishNotification = refreshInfo 1906 return nil 1907 }) 1908 defer restoreFinishRefreshNotification() 1909 1910 var called int 1911 restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) { 1912 called++ 1913 if called < 2 { 1914 return runinhibit.HintInhibitedForRefresh, nil 1915 } 1916 return runinhibit.HintNotInhibited, nil 1917 }) 1918 defer restore() 1919 1920 c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedForRefresh), check.IsNil) 1921 c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.IsNil) 1922 c.Check(called, check.Equals, 2) 1923 1924 c.Check(s.Stdout(), check.Equals, "") 1925 c.Check(notification, check.DeepEquals, &usersessionclient.PendingSnapRefreshInfo{ 1926 InstanceName: "some-snap", 1927 TimeRemaining: 0, 1928 }) 1929 c.Check(finishNotification, check.DeepEquals, &usersessionclient.FinishedSnapRefreshInfo{ 1930 InstanceName: "some-snap", 1931 }) 1932 } 1933 1934 func (s *RunSuite) TestWaitWhileInhibitedGraphicalSessionFlowError(c *check.C) { 1935 restoreIsGraphicalSession := snaprun.MockIsGraphicalSession(true) 1936 defer restoreIsGraphicalSession() 1937 1938 restorePendingRefreshNotification := snaprun.MockPendingRefreshNotification(func(refreshInfo *usersessionclient.PendingSnapRefreshInfo) error { 1939 return fmt.Errorf("boom") 1940 }) 1941 defer restorePendingRefreshNotification() 1942 1943 c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedForRefresh), check.IsNil) 1944 restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) { 1945 return runinhibit.HintInhibitedForRefresh, nil 1946 }) 1947 defer restore() 1948 1949 c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.ErrorMatches, "boom") 1950 } 1951 1952 func (s *RunSuite) TestWaitWhileInhibitedGraphicalSessionFlowErrorOnFinish(c *check.C) { 1953 restoreIsGraphicalSession := snaprun.MockIsGraphicalSession(true) 1954 defer restoreIsGraphicalSession() 1955 1956 restorePendingRefreshNotification := snaprun.MockPendingRefreshNotification(func(refreshInfo *usersessionclient.PendingSnapRefreshInfo) error { 1957 return nil 1958 }) 1959 defer restorePendingRefreshNotification() 1960 1961 restoreFinishRefreshNotification := snaprun.MockFinishRefreshNotification(func(refreshInfo *usersessionclient.FinishedSnapRefreshInfo) error { 1962 return fmt.Errorf("boom") 1963 }) 1964 defer restoreFinishRefreshNotification() 1965 1966 c.Assert(runinhibit.LockWithHint("some-snap", runinhibit.HintInhibitedForRefresh), check.IsNil) 1967 n := 0 1968 restore := snaprun.MockIsLocked(func(snapName string) (runinhibit.Hint, error) { 1969 n++ 1970 if n == 1 { 1971 return runinhibit.HintInhibitedForRefresh, nil 1972 } 1973 return runinhibit.HintNotInhibited, nil 1974 }) 1975 defer restore() 1976 1977 c.Assert(snaprun.WaitWhileInhibited("some-snap"), check.ErrorMatches, "boom") 1978 } 1979 1980 func (s *RunSuite) TestCreateSnapDirPermissions(c *check.C) { 1981 usr, err := user.Current() 1982 c.Assert(err, check.IsNil) 1983 1984 usr.HomeDir = s.fakeHome 1985 snaprun.MockUserCurrent(func() (*user.User, error) { 1986 return usr, nil 1987 }) 1988 1989 info := &snap.Info{SuggestedName: "some-snap"} 1990 c.Assert(snaprun.CreateUserDataDirs(info, nil), check.IsNil) 1991 1992 fi, err := os.Stat(filepath.Join(s.fakeHome, dirs.UserHomeSnapDir)) 1993 c.Assert(err, check.IsNil) 1994 c.Assert(fi.Mode()&os.ModePerm, check.Equals, os.FileMode(0700)) 1995 } 1996 1997 func (s *RunSuite) TestGetSnapDirOptions(c *check.C) { 1998 root := c.MkDir() 1999 dirs.SnapSeqDir = root 2000 dirs.FeaturesDir = root 2001 2002 // write sequence file 2003 seqFile := filepath.Join(dirs.SnapSeqDir, "somesnap.json") 2004 str := struct { 2005 MigratedHidden bool `json:"migrated-hidden"` 2006 MigratedToExposedHome bool `json:"migrated-exposed-home"` 2007 }{ 2008 MigratedHidden: true, 2009 MigratedToExposedHome: true, 2010 } 2011 data, err := json.Marshal(&str) 2012 c.Assert(err, check.IsNil) 2013 c.Assert(ioutil.WriteFile(seqFile, data, 0660), check.IsNil) 2014 2015 // write control file for hidden dir feature 2016 c.Assert(ioutil.WriteFile(features.HiddenSnapDataHomeDir.ControlFile(), []byte{}, 0660), check.IsNil) 2017 2018 opts, err := snaprun.GetSnapDirOptions("somesnap") 2019 c.Assert(err, check.IsNil) 2020 c.Assert(opts, check.DeepEquals, &dirs.SnapDirOptions{HiddenSnapDataDir: true, MigratedToExposedHome: true}) 2021 } 2022 2023 func (s *RunSuite) TestRunDebugLog(c *check.C) { 2024 oldDebug, isSet := os.LookupEnv("SNAPD_DEBUG") 2025 if isSet { 2026 defer os.Setenv("SNAPD_DEBUG", oldDebug) 2027 } else { 2028 defer os.Unsetenv("SNAPD_DEBUG") 2029 } 2030 2031 logBuf, r := logger.MockLogger() 2032 defer r() 2033 2034 restore := mockSnapConfine(dirs.DistroLibExecDir) 2035 defer restore() 2036 execArg0 := "" 2037 execArgs := []string{} 2038 execEnv := []string{} 2039 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 2040 execArg0 = arg0 2041 execArgs = args 2042 execEnv = envv 2043 return nil 2044 }) 2045 defer restore() 2046 2047 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 2048 Revision: snap.R("12"), 2049 }) 2050 2051 // this will modify the current process environment 2052 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--debug-log", "snapname.app"}) 2053 c.Assert(err, check.IsNil) 2054 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 2055 c.Check(execArgs, check.DeepEquals, []string{ 2056 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 2057 "snap.snapname.app", 2058 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 2059 "snapname.app"}) 2060 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=12") 2061 c.Check(execEnv, testutil.Contains, "SNAPD_DEBUG=1") 2062 // also set in env 2063 c.Check(os.Getenv("SNAPD_DEBUG"), check.Equals, "1") 2064 // and we've let the user know that logging was enabled 2065 c.Check(logBuf.String(), testutil.Contains, "DEBUG: enabled debug logging of early snap startup") 2066 }