github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/snap/cmd_run_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 "errors" 24 "fmt" 25 "os" 26 "os/user" 27 "path/filepath" 28 "strings" 29 "time" 30 31 "gopkg.in/check.v1" 32 33 snaprun "github.com/snapcore/snapd/cmd/snap" 34 "github.com/snapcore/snapd/dirs" 35 "github.com/snapcore/snapd/logger" 36 "github.com/snapcore/snapd/osutil" 37 "github.com/snapcore/snapd/sandbox/cgroup" 38 "github.com/snapcore/snapd/sandbox/selinux" 39 "github.com/snapcore/snapd/snap" 40 "github.com/snapcore/snapd/snap/snaptest" 41 "github.com/snapcore/snapd/testutil" 42 "github.com/snapcore/snapd/x11" 43 ) 44 45 var mockYaml = []byte(`name: snapname 46 version: 1.0 47 apps: 48 app: 49 command: run-app 50 svc: 51 command: run-svc 52 daemon: simple 53 hooks: 54 configure: 55 `) 56 57 var mockYamlBaseNone1 = []byte(`name: snapname1 58 version: 1.0 59 base: none 60 apps: 61 app: 62 command: run-app 63 `) 64 65 var mockYamlBaseNone2 = []byte(`name: snapname2 66 version: 1.0 67 base: none 68 hooks: 69 configure: 70 `) 71 72 type RunSuite struct { 73 fakeHome string 74 BaseSnapSuite 75 } 76 77 var _ = check.Suite(&RunSuite{}) 78 79 func (s *RunSuite) SetUpTest(c *check.C) { 80 s.BaseSnapSuite.SetUpTest(c) 81 s.fakeHome = c.MkDir() 82 83 u, err := user.Current() 84 c.Assert(err, check.IsNil) 85 s.AddCleanup(snaprun.MockUserCurrent(func() (*user.User, error) { 86 return &user.User{Uid: u.Uid, HomeDir: s.fakeHome}, nil 87 })) 88 s.AddCleanup(snaprun.MockCreateTransientScopeForTracking(func(string, *cgroup.TrackingOptions) error { 89 return nil 90 })) 91 } 92 93 func (s *RunSuite) TestInvalidParameters(c *check.C) { 94 invalidParameters := []string{"run", "--hook=configure", "--command=command-name", "--", "snap-name"} 95 _, err := snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 96 c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*") 97 98 invalidParameters = []string{"run", "--hook=configure", "--timer=10:00-12:00", "--", "snap-name"} 99 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 100 c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*") 101 102 invalidParameters = []string{"run", "--command=command-name", "--timer=10:00-12:00", "--", "snap-name"} 103 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 104 c.Check(err, check.ErrorMatches, ".*you can only use one of --hook, --command, and --timer.*") 105 106 invalidParameters = []string{"run", "-r=1", "--command=command-name", "--", "snap-name"} 107 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 108 c.Check(err, check.ErrorMatches, ".*-r can only be used with --hook.*") 109 110 invalidParameters = []string{"run", "-r=1", "--", "snap-name"} 111 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 112 c.Check(err, check.ErrorMatches, ".*-r can only be used with --hook.*") 113 114 invalidParameters = []string{"run", "--hook=configure", "--", "foo", "bar", "snap-name"} 115 _, err = snaprun.Parser(snaprun.Client()).ParseArgs(invalidParameters) 116 c.Check(err, check.ErrorMatches, ".*too many arguments for hook \"configure\": bar.*") 117 } 118 119 func (s *RunSuite) TestRunCmdWithBaseNone(c *check.C) { 120 defer mockSnapConfine(dirs.DistroLibExecDir)() 121 122 // mock installed snap 123 snaptest.MockSnapCurrent(c, string(mockYamlBaseNone1), &snap.SideInfo{ 124 Revision: snap.R("1"), 125 }) 126 snaptest.MockSnapCurrent(c, string(mockYamlBaseNone2), &snap.SideInfo{ 127 Revision: snap.R("1"), 128 }) 129 130 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname1.app", "--arg1", "arg2"}) 131 c.Assert(err, check.ErrorMatches, `cannot run hooks / applications with base \"none\"`) 132 133 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname2"}) 134 c.Assert(err, check.ErrorMatches, `cannot run hooks / applications with base \"none\"`) 135 } 136 137 func (s *RunSuite) TestSnapRunWhenMissingConfine(c *check.C) { 138 _, r := logger.MockLogger() 139 defer r() 140 141 // mock installed snap 142 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 143 Revision: snap.R("x2"), 144 }) 145 146 // redirect exec 147 var execs [][]string 148 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 149 execs = append(execs, args) 150 return nil 151 }) 152 defer restorer() 153 154 // and run it! 155 // a regular run will fail 156 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 157 c.Assert(err, check.ErrorMatches, `.* your core/snapd package`) 158 // a hook run will not fail 159 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"}) 160 c.Assert(err, check.IsNil) 161 162 // but nothing is run ever 163 c.Check(execs, check.IsNil) 164 } 165 166 func (s *RunSuite) TestSnapRunAppIntegration(c *check.C) { 167 defer mockSnapConfine(dirs.DistroLibExecDir)() 168 169 tmpdir := os.Getenv("TMPDIR") 170 if tmpdir == "" { 171 tmpdir = "/var/tmp" 172 os.Setenv("TMPDIR", tmpdir) 173 defer os.Unsetenv("TMPDIR") 174 } 175 176 // mock installed snap 177 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 178 Revision: snap.R("x2"), 179 }) 180 181 // redirect exec 182 execArg0 := "" 183 execArgs := []string{} 184 execEnv := []string{} 185 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 186 execArg0 = arg0 187 execArgs = args 188 execEnv = envv 189 return nil 190 }) 191 defer restorer() 192 193 // and run it! 194 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 195 c.Assert(err, check.IsNil) 196 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 197 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 198 c.Check(execArgs, check.DeepEquals, []string{ 199 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 200 "snap.snapname.app", 201 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 202 "snapname.app", "--arg1", "arg2"}) 203 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 204 c.Check(execEnv, testutil.Contains, fmt.Sprintf("TMPDIR=%s", tmpdir)) 205 } 206 207 func (s *RunSuite) TestSnapRunClassicAppIntegration(c *check.C) { 208 defer mockSnapConfine(dirs.DistroLibExecDir)() 209 210 tmpdir := os.Getenv("TMPDIR") 211 if tmpdir == "" { 212 tmpdir = "/var/tmp" 213 os.Setenv("TMPDIR", tmpdir) 214 defer os.Unsetenv("TMPDIR") 215 } 216 217 // mock installed snap 218 snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{ 219 Revision: snap.R("x2"), 220 }) 221 222 // redirect exec 223 execArg0 := "" 224 execArgs := []string{} 225 execEnv := []string{} 226 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 227 execArg0 = arg0 228 execArgs = args 229 execEnv = envv 230 return nil 231 }) 232 defer restorer() 233 234 // and run it! 235 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 236 c.Assert(err, check.IsNil) 237 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 238 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 239 c.Check(execArgs, check.DeepEquals, []string{ 240 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), "--classic", 241 "snap.snapname.app", 242 filepath.Join(dirs.DistroLibExecDir, "snap-exec"), 243 "snapname.app", "--arg1", "arg2"}) 244 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 245 c.Check(execEnv, testutil.Contains, fmt.Sprintf("SNAP_SAVED_TMPDIR=%s", tmpdir)) 246 } 247 248 func (s *RunSuite) TestSnapRunClassicAppIntegrationReexecedFromCore(c *check.C) { 249 mountedCorePath := filepath.Join(dirs.SnapMountDir, "core/current") 250 mountedCoreLibExecPath := filepath.Join(mountedCorePath, dirs.CoreLibExecDir) 251 252 defer mockSnapConfine(mountedCoreLibExecPath)() 253 254 // mock installed snap 255 snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{ 256 Revision: snap.R("x2"), 257 }) 258 259 restore := snaprun.MockOsReadlink(func(name string) (string, error) { 260 // pretend 'snap' is reexeced from 'core' 261 return filepath.Join(mountedCorePath, "usr/bin/snap"), nil 262 }) 263 defer restore() 264 265 execArgs := []string{} 266 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 267 execArgs = args 268 return nil 269 }) 270 defer restorer() 271 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 272 c.Assert(err, check.IsNil) 273 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 274 c.Check(execArgs, check.DeepEquals, []string{ 275 filepath.Join(mountedCoreLibExecPath, "snap-confine"), "--classic", 276 "snap.snapname.app", 277 filepath.Join(mountedCoreLibExecPath, "snap-exec"), 278 "snapname.app", "--arg1", "arg2"}) 279 } 280 281 func (s *RunSuite) TestSnapRunClassicAppIntegrationReexecedFromSnapd(c *check.C) { 282 mountedSnapdPath := filepath.Join(dirs.SnapMountDir, "snapd/current") 283 mountedSnapdLibExecPath := filepath.Join(mountedSnapdPath, dirs.CoreLibExecDir) 284 285 defer mockSnapConfine(mountedSnapdLibExecPath)() 286 287 // mock installed snap 288 snaptest.MockSnapCurrent(c, string(mockYaml)+"confinement: classic\n", &snap.SideInfo{ 289 Revision: snap.R("x2"), 290 }) 291 292 restore := snaprun.MockOsReadlink(func(name string) (string, error) { 293 // pretend 'snap' is reexeced from 'core' 294 return filepath.Join(mountedSnapdPath, "usr/bin/snap"), nil 295 }) 296 defer restore() 297 298 execArgs := []string{} 299 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 300 execArgs = args 301 return nil 302 }) 303 defer restorer() 304 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 305 c.Assert(err, check.IsNil) 306 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 307 c.Check(execArgs, check.DeepEquals, []string{ 308 filepath.Join(mountedSnapdLibExecPath, "snap-confine"), "--classic", 309 "snap.snapname.app", 310 filepath.Join(mountedSnapdLibExecPath, "snap-exec"), 311 "snapname.app", "--arg1", "arg2"}) 312 } 313 314 func (s *RunSuite) TestSnapRunAppWithCommandIntegration(c *check.C) { 315 defer mockSnapConfine(dirs.DistroLibExecDir)() 316 317 // mock installed snap 318 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 319 Revision: snap.R(42), 320 }) 321 322 // redirect exec 323 execArg0 := "" 324 execArgs := []string{} 325 execEnv := []string{} 326 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 327 execArg0 = arg0 328 execArgs = args 329 execEnv = envv 330 return nil 331 }) 332 defer restorer() 333 334 // and run it! 335 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=my-command", "--", "snapname.app", "arg1", "arg2"}) 336 c.Assert(err, check.IsNil) 337 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 338 c.Check(execArgs, check.DeepEquals, []string{ 339 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 340 "snap.snapname.app", 341 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 342 "--command=my-command", "snapname.app", "arg1", "arg2"}) 343 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 344 } 345 346 func (s *RunSuite) TestSnapRunCreateDataDirs(c *check.C) { 347 info, err := snap.InfoFromSnapYaml(mockYaml) 348 c.Assert(err, check.IsNil) 349 info.SideInfo.Revision = snap.R(42) 350 351 err = snaprun.CreateUserDataDirs(info) 352 c.Assert(err, check.IsNil) 353 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname/42")), check.Equals, true) 354 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname/common")), check.Equals, true) 355 } 356 357 func (s *RunSuite) TestParallelInstanceSnapRunCreateDataDirs(c *check.C) { 358 info, err := snap.InfoFromSnapYaml(mockYaml) 359 c.Assert(err, check.IsNil) 360 info.SideInfo.Revision = snap.R(42) 361 info.InstanceKey = "foo" 362 363 err = snaprun.CreateUserDataDirs(info) 364 c.Assert(err, check.IsNil) 365 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/42")), check.Equals, true) 366 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname_foo/common")), check.Equals, true) 367 // mount point for snap instance mapping has been created 368 c.Check(osutil.FileExists(filepath.Join(s.fakeHome, "/snap/snapname")), check.Equals, true) 369 // and it's empty inside 370 m, err := filepath.Glob(filepath.Join(s.fakeHome, "/snap/snapname/*")) 371 c.Assert(err, check.IsNil) 372 c.Assert(m, check.HasLen, 0) 373 } 374 375 func (s *RunSuite) TestSnapRunHookIntegration(c *check.C) { 376 defer mockSnapConfine(dirs.DistroLibExecDir)() 377 378 // mock installed snap 379 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 380 Revision: snap.R(42), 381 }) 382 383 // redirect exec 384 execArg0 := "" 385 execArgs := []string{} 386 execEnv := []string{} 387 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 388 execArg0 = arg0 389 execArgs = args 390 execEnv = envv 391 return nil 392 }) 393 defer restorer() 394 395 // Run a hook from the active revision 396 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "--", "snapname"}) 397 c.Assert(err, check.IsNil) 398 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 399 c.Check(execArgs, check.DeepEquals, []string{ 400 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 401 "snap.snapname.hook.configure", 402 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 403 "--hook=configure", "snapname"}) 404 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 405 } 406 407 func (s *RunSuite) TestSnapRunHookUnsetRevisionIntegration(c *check.C) { 408 defer mockSnapConfine(dirs.DistroLibExecDir)() 409 410 // mock installed snap 411 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 412 Revision: snap.R(42), 413 }) 414 415 // redirect exec 416 execArg0 := "" 417 execArgs := []string{} 418 execEnv := []string{} 419 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 420 execArg0 = arg0 421 execArgs = args 422 execEnv = envv 423 return nil 424 }) 425 defer restorer() 426 427 // Specifically pass "unset" which would use the active version. 428 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=unset", "--", "snapname"}) 429 c.Assert(err, check.IsNil) 430 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 431 c.Check(execArgs, check.DeepEquals, []string{ 432 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 433 "snap.snapname.hook.configure", 434 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 435 "--hook=configure", "snapname"}) 436 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 437 } 438 439 func (s *RunSuite) TestSnapRunHookSpecificRevisionIntegration(c *check.C) { 440 defer mockSnapConfine(dirs.DistroLibExecDir)() 441 442 // mock installed snap 443 // Create both revisions 41 and 42 444 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 445 Revision: snap.R(41), 446 }) 447 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 448 Revision: snap.R(42), 449 }) 450 451 // redirect exec 452 execArg0 := "" 453 execArgs := []string{} 454 execEnv := []string{} 455 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 456 execArg0 = arg0 457 execArgs = args 458 execEnv = envv 459 return nil 460 }) 461 defer restorer() 462 463 // Run a hook on revision 41 464 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=41", "--", "snapname"}) 465 c.Assert(err, check.IsNil) 466 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 467 c.Check(execArgs, check.DeepEquals, []string{ 468 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 469 "snap.snapname.hook.configure", 470 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 471 "--hook=configure", "snapname"}) 472 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=41") 473 } 474 475 func (s *RunSuite) TestSnapRunHookMissingRevisionIntegration(c *check.C) { 476 // Only create revision 42 477 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 478 Revision: snap.R(42), 479 }) 480 481 // redirect exec 482 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 483 return nil 484 }) 485 defer restorer() 486 487 // Attempt to run a hook on revision 41, which doesn't exist 488 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=41", "--", "snapname"}) 489 c.Assert(err, check.NotNil) 490 c.Check(err, check.ErrorMatches, "cannot find .*") 491 } 492 493 func (s *RunSuite) TestSnapRunHookInvalidRevisionIntegration(c *check.C) { 494 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=configure", "-r=invalid", "--", "snapname"}) 495 c.Assert(err, check.NotNil) 496 c.Check(err, check.ErrorMatches, "invalid snap revision: \"invalid\"") 497 } 498 499 func (s *RunSuite) TestSnapRunHookMissingHookIntegration(c *check.C) { 500 // Only create revision 42 501 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 502 Revision: snap.R(42), 503 }) 504 505 // redirect exec 506 called := false 507 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 508 called = true 509 return nil 510 }) 511 defer restorer() 512 513 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook=missing-hook", "--", "snapname"}) 514 c.Assert(err, check.ErrorMatches, `cannot find hook "missing-hook" in "snapname"`) 515 c.Check(called, check.Equals, false) 516 } 517 518 func (s *RunSuite) TestSnapRunErorsForUnknownRunArg(c *check.C) { 519 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--unknown", "--", "snapname.app", "--arg1", "arg2"}) 520 c.Assert(err, check.ErrorMatches, "unknown flag `unknown'") 521 } 522 523 func (s *RunSuite) TestSnapRunErorsForMissingApp(c *check.C) { 524 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--command=shell"}) 525 c.Assert(err, check.ErrorMatches, "need the application to run as argument") 526 } 527 528 func (s *RunSuite) TestSnapRunErorrForUnavailableApp(c *check.C) { 529 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "not-there"}) 530 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)) 531 } 532 533 func (s *RunSuite) TestSnapRunSaneEnvironmentHandling(c *check.C) { 534 defer mockSnapConfine(dirs.DistroLibExecDir)() 535 536 // mock installed snap 537 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 538 Revision: snap.R(42), 539 }) 540 541 // redirect exec 542 execEnv := []string{} 543 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 544 execEnv = envv 545 return nil 546 }) 547 defer restorer() 548 549 // set a SNAP{,_*} variable in the environment 550 os.Setenv("SNAP_NAME", "something-else") 551 os.Setenv("SNAP_ARCH", "PDP-7") 552 defer os.Unsetenv("SNAP_NAME") 553 defer os.Unsetenv("SNAP_ARCH") 554 // but unrelated stuff is ok 555 os.Setenv("SNAP_THE_WORLD", "YES") 556 defer os.Unsetenv("SNAP_THE_WORLD") 557 558 // and ensure those SNAP_ vars get overridden 559 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 560 c.Assert(err, check.IsNil) 561 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 562 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=42") 563 c.Check(execEnv, check.Not(testutil.Contains), "SNAP_NAME=something-else") 564 c.Check(execEnv, check.Not(testutil.Contains), "SNAP_ARCH=PDP-7") 565 c.Check(execEnv, testutil.Contains, "SNAP_THE_WORLD=YES") 566 } 567 568 func (s *RunSuite) TestSnapRunSnapdHelperPath(c *check.C) { 569 _, r := logger.MockLogger() 570 defer r() 571 572 var osReadlinkResult string 573 restore := snaprun.MockOsReadlink(func(name string) (string, error) { 574 return osReadlinkResult, nil 575 }) 576 defer restore() 577 578 tool := "snap-confine" 579 for _, t := range []struct { 580 readlink string 581 expected string 582 }{ 583 { 584 filepath.Join(dirs.SnapMountDir, "core/current/usr/bin/snap"), 585 filepath.Join(dirs.SnapMountDir, "core/current", dirs.CoreLibExecDir, tool), 586 }, 587 { 588 filepath.Join(dirs.SnapMountDir, "snapd/current/usr/bin/snap"), 589 filepath.Join(dirs.SnapMountDir, "snapd/current", dirs.CoreLibExecDir, tool), 590 }, 591 { 592 filepath.Join("/usr/bin/snap"), 593 filepath.Join(dirs.DistroLibExecDir, tool), 594 }, 595 { 596 filepath.Join("/home/foo/ws/snapd/snap"), 597 filepath.Join(dirs.DistroLibExecDir, tool), 598 }, 599 // unexpected case 600 { 601 filepath.Join(dirs.SnapMountDir, "snapd2/current/bin/snap"), 602 filepath.Join(dirs.DistroLibExecDir, tool), 603 }, 604 } { 605 osReadlinkResult = t.readlink 606 toolPath, err := snaprun.SnapdHelperPath(tool) 607 c.Assert(err, check.IsNil) 608 c.Check(toolPath, check.Equals, t.expected) 609 } 610 } 611 612 func (s *RunSuite) TestSnapRunAppIntegrationFromCore(c *check.C) { 613 defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir))() 614 615 // mock installed snap 616 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 617 Revision: snap.R("x2"), 618 }) 619 620 // pretend to be running from core 621 restorer := snaprun.MockOsReadlink(func(string) (string, error) { 622 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 623 }) 624 defer restorer() 625 626 // redirect exec 627 execArg0 := "" 628 execArgs := []string{} 629 execEnv := []string{} 630 restorer = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 631 execArg0 = arg0 632 execArgs = args 633 execEnv = envv 634 return nil 635 }) 636 defer restorer() 637 638 // and run it! 639 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 640 c.Assert(err, check.IsNil) 641 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 642 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 643 c.Check(execArgs, check.DeepEquals, []string{ 644 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 645 "snap.snapname.app", 646 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 647 "snapname.app", "--arg1", "arg2"}) 648 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 649 } 650 651 func (s *RunSuite) TestSnapRunAppIntegrationFromSnapd(c *check.C) { 652 defer mockSnapConfine(filepath.Join(dirs.SnapMountDir, "snapd", "222", dirs.CoreLibExecDir))() 653 654 // mock installed snap 655 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 656 Revision: snap.R("x2"), 657 }) 658 659 // pretend to be running from snapd 660 restorer := snaprun.MockOsReadlink(func(string) (string, error) { 661 return filepath.Join(dirs.SnapMountDir, "snapd/222/usr/bin/snap"), nil 662 }) 663 defer restorer() 664 665 // redirect exec 666 execArg0 := "" 667 execArgs := []string{} 668 execEnv := []string{} 669 restorer = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 670 execArg0 = arg0 671 execArgs = args 672 execEnv = envv 673 return nil 674 }) 675 defer restorer() 676 677 // and run it! 678 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 679 c.Assert(err, check.IsNil) 680 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 681 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/snapd/222", dirs.CoreLibExecDir, "snap-confine")) 682 c.Check(execArgs, check.DeepEquals, []string{ 683 filepath.Join(dirs.SnapMountDir, "/snapd/222", dirs.CoreLibExecDir, "snap-confine"), 684 "snap.snapname.app", 685 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 686 "snapname.app", "--arg1", "arg2"}) 687 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 688 } 689 690 func (s *RunSuite) TestSnapRunXauthorityMigration(c *check.C) { 691 defer mockSnapConfine(dirs.DistroLibExecDir)() 692 693 u, err := user.Current() 694 c.Assert(err, check.IsNil) 695 696 // Ensure XDG_RUNTIME_DIR exists for the user we're testing with 697 err = os.MkdirAll(filepath.Join(dirs.XdgRuntimeDirBase, u.Uid), 0700) 698 c.Assert(err, check.IsNil) 699 700 // mock installed snap; happily this also gives us a directory 701 // below /tmp which the Xauthority migration expects. 702 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 703 Revision: snap.R("x2"), 704 }) 705 706 // redirect exec 707 execArg0 := "" 708 execArgs := []string{} 709 execEnv := []string{} 710 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 711 execArg0 = arg0 712 execArgs = args 713 execEnv = envv 714 return nil 715 }) 716 defer restorer() 717 718 xauthPath, err := x11.MockXauthority(2) 719 c.Assert(err, check.IsNil) 720 defer os.Remove(xauthPath) 721 722 defer snaprun.MockGetEnv(func(name string) string { 723 if name == "XAUTHORITY" { 724 return xauthPath 725 } 726 return "" 727 })() 728 729 // and run it! 730 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 731 c.Assert(err, check.IsNil) 732 c.Assert(rest, check.DeepEquals, []string{"snapname.app"}) 733 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 734 c.Check(execArgs, check.DeepEquals, []string{ 735 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 736 "snap.snapname.app", 737 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 738 "snapname.app"}) 739 740 expectedXauthPath := filepath.Join(dirs.XdgRuntimeDirBase, u.Uid, ".Xauthority") 741 c.Check(execEnv, testutil.Contains, fmt.Sprintf("XAUTHORITY=%s", expectedXauthPath)) 742 743 info, err := os.Stat(expectedXauthPath) 744 c.Assert(err, check.IsNil) 745 c.Assert(info.Mode().Perm(), check.Equals, os.FileMode(0600)) 746 747 err = x11.ValidateXauthorityFile(expectedXauthPath) 748 c.Assert(err, check.IsNil) 749 } 750 751 // build the args for a hypothetical completer 752 func mkCompArgs(compPoint string, argv ...string) []string { 753 out := []string{ 754 "99", // COMP_TYPE 755 "99", // COMP_KEY 756 "", // COMP_POINT 757 "2", // COMP_CWORD 758 " ", // COMP_WORDBREAKS 759 } 760 out[2] = compPoint 761 out = append(out, strings.Join(argv, " ")) 762 out = append(out, argv...) 763 return out 764 } 765 766 func (s *RunSuite) TestAntialiasHappy(c *check.C) { 767 c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil) 768 769 inArgs := mkCompArgs("10", "alias", "alias", "bo-alias") 770 771 // first not so happy because no alias symlink 772 app, outArgs := snaprun.Antialias("alias", inArgs) 773 c.Check(app, check.Equals, "alias") 774 c.Check(outArgs, check.DeepEquals, inArgs) 775 776 c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil) 777 778 // now really happy 779 app, outArgs = snaprun.Antialias("alias", inArgs) 780 c.Check(app, check.Equals, "an-app") 781 c.Check(outArgs, check.DeepEquals, []string{ 782 "99", // COMP_TYPE (no change) 783 "99", // COMP_KEY (no change) 784 "11", // COMP_POINT (+1 because "an-app" is one longer than "alias") 785 "2", // COMP_CWORD (no change) 786 " ", // COMP_WORDBREAKS (no change) 787 "an-app alias bo-alias", // COMP_LINE (argv[0] changed) 788 "an-app", // argv (arv[0] changed) 789 "alias", 790 "bo-alias", 791 }) 792 } 793 794 func (s *RunSuite) TestAntialiasBailsIfUnhappy(c *check.C) { 795 // alias exists but args are somehow wonky 796 c.Assert(os.MkdirAll(dirs.SnapBinariesDir, 0755), check.IsNil) 797 c.Assert(os.Symlink("an-app", filepath.Join(dirs.SnapBinariesDir, "alias")), check.IsNil) 798 799 // weird1 has COMP_LINE not start with COMP_WORDS[0], argv[0] equal to COMP_WORDS[0] 800 weird1 := mkCompArgs("6", "alias", "") 801 weird1[5] = "xxxxx " 802 // weird2 has COMP_LINE not start with COMP_WORDS[0], argv[0] equal to the first word in COMP_LINE 803 weird2 := mkCompArgs("6", "xxxxx", "") 804 weird2[5] = "alias " 805 806 for desc, inArgs := range map[string][]string{ 807 "nil args": nil, 808 "too-short args": {"alias"}, 809 "COMP_POINT not a number": mkCompArgs("hello", "alias"), 810 "COMP_POINT is inside argv[0]": mkCompArgs("2", "alias", ""), 811 "COMP_POINT is outside argv": mkCompArgs("99", "alias", ""), 812 "COMP_WORDS[0] is not argv[0]": mkCompArgs("10", "not-alias", ""), 813 "mismatch between argv[0], COMP_LINE and COMP_WORDS, #1": weird1, 814 "mismatch between argv[0], COMP_LINE and COMP_WORDS, #2": weird2, 815 } { 816 // antialias leaves args alone if it's too short 817 app, outArgs := snaprun.Antialias("alias", inArgs) 818 c.Check(app, check.Equals, "alias", check.Commentf(desc)) 819 c.Check(outArgs, check.DeepEquals, inArgs, check.Commentf(desc)) 820 } 821 } 822 823 func (s *RunSuite) TestSnapRunAppWithStraceIntegration(c *check.C) { 824 defer mockSnapConfine(dirs.DistroLibExecDir)() 825 826 // mock installed snap 827 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 828 Revision: snap.R("x2"), 829 }) 830 831 // pretend we have sudo and simulate some useful output that would 832 // normally come from strace 833 sudoCmd := testutil.MockCommand(c, "sudo", fmt.Sprintf(` 834 echo "stdout output 1" 835 >&2 echo 'execve("/path/to/snap-confine")' 836 >&2 echo "snap-confine/snap-exec strace stuff" 837 >&2 echo "getuid() = 1000" 838 >&2 echo 'execve("%s/snapName/x2/bin/foo")' 839 >&2 echo "interessting strace output" 840 >&2 echo "and more" 841 echo "stdout output 2" 842 `, dirs.SnapMountDir)) 843 defer sudoCmd.Restore() 844 845 // pretend we have strace 846 straceCmd := testutil.MockCommand(c, "strace", "") 847 defer straceCmd.Restore() 848 849 user, err := user.Current() 850 c.Assert(err, check.IsNil) 851 852 // and run it under strace 853 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--strace", "--", "snapname.app", "--arg1", "arg2"}) 854 c.Assert(err, check.IsNil) 855 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 856 c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{ 857 { 858 "sudo", "-E", 859 filepath.Join(straceCmd.BinDir(), "strace"), 860 "-u", user.Username, 861 "-f", 862 "-e", "!select,pselect6,_newselect,clock_gettime,sigaltstack,gettid,gettimeofday,nanosleep", 863 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 864 "snap.snapname.app", 865 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 866 "snapname.app", "--arg1", "arg2", 867 }, 868 }) 869 c.Check(s.Stdout(), check.Equals, "stdout output 1\nstdout output 2\n") 870 c.Check(s.Stderr(), check.Equals, fmt.Sprintf("execve(%q)\ninteressting strace output\nand more\n", filepath.Join(dirs.SnapMountDir, "snapName/x2/bin/foo"))) 871 872 s.ResetStdStreams() 873 sudoCmd.ForgetCalls() 874 875 // try again without filtering 876 rest, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--strace=--raw", "--", "snapname.app", "--arg1", "arg2"}) 877 c.Assert(err, check.IsNil) 878 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 879 c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{ 880 { 881 "sudo", "-E", 882 filepath.Join(straceCmd.BinDir(), "strace"), 883 "-u", user.Username, 884 "-f", 885 "-e", "!select,pselect6,_newselect,clock_gettime,sigaltstack,gettid,gettimeofday,nanosleep", 886 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 887 "snap.snapname.app", 888 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 889 "snapname.app", "--arg1", "arg2", 890 }, 891 }) 892 c.Check(s.Stdout(), check.Equals, "stdout output 1\nstdout output 2\n") 893 expectedFullFmt := `execve("/path/to/snap-confine") 894 snap-confine/snap-exec strace stuff 895 getuid() = 1000 896 execve("%s/snapName/x2/bin/foo") 897 interessting strace output 898 and more 899 ` 900 c.Check(s.Stderr(), check.Equals, fmt.Sprintf(expectedFullFmt, dirs.SnapMountDir)) 901 } 902 903 func (s *RunSuite) TestSnapRunAppWithStraceOptions(c *check.C) { 904 defer mockSnapConfine(dirs.DistroLibExecDir)() 905 906 // mock installed snap 907 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 908 Revision: snap.R("x2"), 909 }) 910 911 // pretend we have sudo 912 sudoCmd := testutil.MockCommand(c, "sudo", "") 913 defer sudoCmd.Restore() 914 915 // pretend we have strace 916 straceCmd := testutil.MockCommand(c, "strace", "") 917 defer straceCmd.Restore() 918 919 user, err := user.Current() 920 c.Assert(err, check.IsNil) 921 922 // and run it under strace 923 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--strace=-tt --raw -o "file with spaces"`, "--", "snapname.app", "--arg1", "arg2"}) 924 c.Assert(err, check.IsNil) 925 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 926 c.Check(sudoCmd.Calls(), check.DeepEquals, [][]string{ 927 { 928 "sudo", "-E", 929 filepath.Join(straceCmd.BinDir(), "strace"), 930 "-u", user.Username, 931 "-f", 932 "-e", "!select,pselect6,_newselect,clock_gettime,sigaltstack,gettid,gettimeofday,nanosleep", 933 "-tt", 934 "-o", 935 "file with spaces", 936 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 937 "snap.snapname.app", 938 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 939 "snapname.app", "--arg1", "arg2", 940 }, 941 }) 942 } 943 944 func (s *RunSuite) TestSnapRunShellIntegration(c *check.C) { 945 defer mockSnapConfine(dirs.DistroLibExecDir)() 946 947 // mock installed snap 948 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 949 Revision: snap.R("x2"), 950 }) 951 952 // redirect exec 953 execArg0 := "" 954 execArgs := []string{} 955 execEnv := []string{} 956 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 957 execArg0 = arg0 958 execArgs = args 959 execEnv = envv 960 return nil 961 }) 962 defer restorer() 963 964 // and run it! 965 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--shell", "--", "snapname.app", "--arg1", "arg2"}) 966 c.Assert(err, check.IsNil) 967 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 968 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 969 c.Check(execArgs, check.DeepEquals, []string{ 970 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 971 "snap.snapname.app", 972 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 973 "--command=shell", "snapname.app", "--arg1", "arg2"}) 974 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 975 } 976 977 func (s *RunSuite) TestSnapRunAppTimer(c *check.C) { 978 defer mockSnapConfine(dirs.DistroLibExecDir)() 979 980 // mock installed snap 981 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 982 Revision: snap.R("x2"), 983 }) 984 985 // redirect exec 986 execArg0 := "" 987 execArgs := []string{} 988 execCalled := false 989 restorer := snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 990 execArg0 = arg0 991 execArgs = args 992 execCalled = true 993 return nil 994 }) 995 defer restorer() 996 997 fakeNow := time.Date(2018, 02, 12, 9, 55, 0, 0, time.Local) 998 restorer = snaprun.MockTimeNow(func() time.Time { 999 // Monday Feb 12, 9:55 1000 return fakeNow 1001 }) 1002 defer restorer() 1003 1004 // pretend we are outside of timer range 1005 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--timer="mon,10:00~12:00,,fri,13:00"`, "--", "snapname.app", "--arg1", "arg2"}) 1006 c.Assert(err, check.IsNil) 1007 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1008 c.Assert(execCalled, check.Equals, false) 1009 1010 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" 1011 `, fakeNow.Format(time.RFC3339))) 1012 s.ResetStdStreams() 1013 1014 restorer = snaprun.MockTimeNow(func() time.Time { 1015 // Monday Feb 12, 10:20 1016 return time.Date(2018, 02, 12, 10, 20, 0, 0, time.Local) 1017 }) 1018 defer restorer() 1019 1020 // and run it under strace 1021 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", `--timer="mon,10:00~12:00,,fri,13:00"`, "--", "snapname.app", "--arg1", "arg2"}) 1022 c.Assert(err, check.IsNil) 1023 c.Assert(execCalled, check.Equals, true) 1024 c.Check(execArg0, check.Equals, filepath.Join(dirs.DistroLibExecDir, "snap-confine")) 1025 c.Check(execArgs, check.DeepEquals, []string{ 1026 filepath.Join(dirs.DistroLibExecDir, "snap-confine"), 1027 "snap.snapname.app", 1028 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1029 "snapname.app", "--arg1", "arg2"}) 1030 } 1031 1032 func (s *RunSuite) TestRunCmdWithTraceExecUnhappy(c *check.C) { 1033 _, r := logger.MockLogger() 1034 defer r() 1035 1036 defer mockSnapConfine(dirs.DistroLibExecDir)() 1037 1038 // mock installed snap 1039 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1040 Revision: snap.R("1"), 1041 }) 1042 1043 // pretend we have sudo 1044 sudoCmd := testutil.MockCommand(c, "sudo", "echo unhappy; exit 12") 1045 defer sudoCmd.Restore() 1046 1047 // pretend we have strace 1048 straceCmd := testutil.MockCommand(c, "strace", "") 1049 defer straceCmd.Restore() 1050 1051 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--trace-exec", "--", "snapname.app", "--arg1", "arg2"}) 1052 c.Assert(err, check.ErrorMatches, "exit status 12") 1053 c.Assert(rest, check.DeepEquals, []string{"--", "snapname.app", "--arg1", "arg2"}) 1054 c.Check(s.Stdout(), check.Equals, "unhappy\n") 1055 c.Check(s.Stderr(), check.Equals, "") 1056 } 1057 1058 func (s *RunSuite) TestSnapRunRestoreSecurityContextHappy(c *check.C) { 1059 logbuf, restorer := logger.MockLogger() 1060 defer restorer() 1061 1062 defer mockSnapConfine(dirs.DistroLibExecDir)() 1063 1064 // mock installed snap 1065 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1066 Revision: snap.R("x2"), 1067 }) 1068 1069 // redirect exec 1070 execCalled := 0 1071 restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error { 1072 execCalled++ 1073 return nil 1074 }) 1075 defer restorer() 1076 1077 verifyCalls := 0 1078 restoreCalls := 0 1079 isEnabledCalls := 0 1080 enabled := false 1081 verify := true 1082 1083 snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir) 1084 1085 restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) { 1086 c.Check(what, check.Equals, snapUserDir) 1087 verifyCalls++ 1088 return verify, nil 1089 }) 1090 defer restorer() 1091 1092 restorer = snaprun.MockSELinuxRestoreContext(func(what string, mode selinux.RestoreMode) error { 1093 c.Check(mode, check.Equals, selinux.RestoreMode{Recursive: true}) 1094 c.Check(what, check.Equals, snapUserDir) 1095 restoreCalls++ 1096 return nil 1097 }) 1098 defer restorer() 1099 1100 restorer = snaprun.MockSELinuxIsEnabled(func() (bool, error) { 1101 isEnabledCalls++ 1102 return enabled, nil 1103 }) 1104 defer restorer() 1105 1106 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1107 c.Assert(err, check.IsNil) 1108 c.Check(execCalled, check.Equals, 1) 1109 c.Check(isEnabledCalls, check.Equals, 1) 1110 c.Check(verifyCalls, check.Equals, 0) 1111 c.Check(restoreCalls, check.Equals, 0) 1112 1113 // pretend SELinux is on 1114 enabled = true 1115 1116 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1117 c.Assert(err, check.IsNil) 1118 c.Check(execCalled, check.Equals, 2) 1119 c.Check(isEnabledCalls, check.Equals, 2) 1120 c.Check(verifyCalls, check.Equals, 1) 1121 c.Check(restoreCalls, check.Equals, 0) 1122 1123 // pretend the context does not match 1124 verify = false 1125 1126 logbuf.Reset() 1127 1128 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1129 c.Assert(err, check.IsNil) 1130 c.Check(execCalled, check.Equals, 3) 1131 c.Check(isEnabledCalls, check.Equals, 3) 1132 c.Check(verifyCalls, check.Equals, 2) 1133 c.Check(restoreCalls, check.Equals, 1) 1134 1135 // and we let the user know what we're doing 1136 c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("restoring default SELinux context of %s", snapUserDir)) 1137 } 1138 1139 func (s *RunSuite) TestSnapRunRestoreSecurityContextFail(c *check.C) { 1140 logbuf, restorer := logger.MockLogger() 1141 defer restorer() 1142 1143 defer mockSnapConfine(dirs.DistroLibExecDir)() 1144 1145 // mock installed snap 1146 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1147 Revision: snap.R("x2"), 1148 }) 1149 1150 // redirect exec 1151 execCalled := 0 1152 restorer = snaprun.MockSyscallExec(func(_ string, args []string, envv []string) error { 1153 execCalled++ 1154 return nil 1155 }) 1156 defer restorer() 1157 1158 verifyCalls := 0 1159 restoreCalls := 0 1160 isEnabledCalls := 0 1161 enabledErr := errors.New("enabled failed") 1162 verifyErr := errors.New("verify failed") 1163 restoreErr := errors.New("restore failed") 1164 1165 snapUserDir := filepath.Join(s.fakeHome, dirs.UserHomeSnapDir) 1166 1167 restorer = snaprun.MockSELinuxVerifyPathContext(func(what string) (bool, error) { 1168 c.Check(what, check.Equals, snapUserDir) 1169 verifyCalls++ 1170 return false, verifyErr 1171 }) 1172 defer restorer() 1173 1174 restorer = snaprun.MockSELinuxRestoreContext(func(what string, mode selinux.RestoreMode) error { 1175 c.Check(mode, check.Equals, selinux.RestoreMode{Recursive: true}) 1176 c.Check(what, check.Equals, snapUserDir) 1177 restoreCalls++ 1178 return restoreErr 1179 }) 1180 defer restorer() 1181 1182 restorer = snaprun.MockSELinuxIsEnabled(func() (bool, error) { 1183 isEnabledCalls++ 1184 return enabledErr == nil, enabledErr 1185 }) 1186 defer restorer() 1187 1188 _, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1189 // these errors are only logged, but we still run the snap 1190 c.Assert(err, check.IsNil) 1191 c.Check(execCalled, check.Equals, 1) 1192 c.Check(logbuf.String(), testutil.Contains, "cannot determine SELinux status: enabled failed") 1193 c.Check(isEnabledCalls, check.Equals, 1) 1194 c.Check(verifyCalls, check.Equals, 0) 1195 c.Check(restoreCalls, check.Equals, 0) 1196 // pretend selinux is on 1197 enabledErr = nil 1198 1199 logbuf.Reset() 1200 1201 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1202 c.Assert(err, check.IsNil) 1203 c.Check(execCalled, check.Equals, 2) 1204 c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("failed to verify SELinux context of %s: verify failed", snapUserDir)) 1205 c.Check(isEnabledCalls, check.Equals, 2) 1206 c.Check(verifyCalls, check.Equals, 1) 1207 c.Check(restoreCalls, check.Equals, 0) 1208 1209 // pretend the context does not match 1210 verifyErr = nil 1211 1212 logbuf.Reset() 1213 1214 _, err = snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app"}) 1215 c.Assert(err, check.IsNil) 1216 c.Check(execCalled, check.Equals, 3) 1217 c.Check(logbuf.String(), testutil.Contains, fmt.Sprintf("cannot restore SELinux context of %s: restore failed", snapUserDir)) 1218 c.Check(isEnabledCalls, check.Equals, 3) 1219 c.Check(verifyCalls, check.Equals, 2) 1220 c.Check(restoreCalls, check.Equals, 1) 1221 } 1222 1223 // systemctl is-system-running returns "running" in normal situations. 1224 func (s *RunSuite) TestIsStoppingRunning(c *check.C) { 1225 systemctl := testutil.MockCommand(c, "systemctl", ` 1226 case "$1" in 1227 is-system-running) 1228 echo "running" 1229 exit 0 1230 ;; 1231 esac 1232 `) 1233 defer systemctl.Restore() 1234 stop, err := snaprun.IsStopping() 1235 c.Check(err, check.IsNil) 1236 c.Check(stop, check.Equals, false) 1237 c.Check(systemctl.Calls(), check.DeepEquals, [][]string{ 1238 {"systemctl", "is-system-running"}, 1239 }) 1240 } 1241 1242 // systemctl is-system-running returns "stopping" when the system is 1243 // shutting down or rebooting. At the same time it returns a non-zero 1244 // exit status. 1245 func (s *RunSuite) TestIsStoppingStopping(c *check.C) { 1246 systemctl := testutil.MockCommand(c, "systemctl", ` 1247 case "$1" in 1248 is-system-running) 1249 echo "stopping" 1250 exit 1 1251 ;; 1252 esac 1253 `) 1254 defer systemctl.Restore() 1255 stop, err := snaprun.IsStopping() 1256 c.Check(err, check.IsNil) 1257 c.Check(stop, check.Equals, true) 1258 c.Check(systemctl.Calls(), check.DeepEquals, [][]string{ 1259 {"systemctl", "is-system-running"}, 1260 }) 1261 } 1262 1263 // systemctl is-system-running can often return "degraded" 1264 // Let's make sure that is not confusing us. 1265 func (s *RunSuite) TestIsStoppingDegraded(c *check.C) { 1266 systemctl := testutil.MockCommand(c, "systemctl", ` 1267 case "$1" in 1268 is-system-running) 1269 echo "degraded" 1270 exit 1 1271 ;; 1272 esac 1273 `) 1274 defer systemctl.Restore() 1275 stop, err := snaprun.IsStopping() 1276 c.Check(err, check.IsNil) 1277 c.Check(stop, check.Equals, false) 1278 c.Check(systemctl.Calls(), check.DeepEquals, [][]string{ 1279 {"systemctl", "is-system-running"}, 1280 }) 1281 } 1282 1283 func (s *RunSuite) TestSnapRunTrackingApps(c *check.C) { 1284 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1285 defer restore() 1286 1287 // mock installed snap 1288 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1289 Revision: snap.R("x2"), 1290 }) 1291 1292 // pretend to be running from core 1293 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1294 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1295 }) 1296 defer restore() 1297 1298 created := false 1299 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1300 c.Assert(securityTag, check.Equals, "snap.snapname.app") 1301 c.Assert(opts, check.NotNil) 1302 c.Assert(opts.AllowSessionBus, check.Equals, true) 1303 created = true 1304 return nil 1305 }) 1306 defer restore() 1307 1308 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1309 panic("apps need to create a scope and do not use systemd service tracking") 1310 }) 1311 defer restore() 1312 1313 // redirect exec 1314 execArg0 := "" 1315 execArgs := []string{} 1316 execEnv := []string{} 1317 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1318 execArg0 = arg0 1319 execArgs = args 1320 execEnv = envv 1321 return nil 1322 }) 1323 defer restore() 1324 1325 // and run it! 1326 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 1327 c.Assert(err, check.IsNil) 1328 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1329 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1330 c.Check(execArgs, check.DeepEquals, []string{ 1331 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1332 "snap.snapname.app", 1333 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1334 "snapname.app", "--arg1", "arg2"}) 1335 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1336 c.Assert(created, check.Equals, true) 1337 } 1338 1339 func (s *RunSuite) TestSnapRunTrackingHooks(c *check.C) { 1340 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1341 defer restore() 1342 1343 // mock installed snap 1344 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1345 Revision: snap.R("x2"), 1346 }) 1347 1348 // pretend to be running from core 1349 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1350 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1351 }) 1352 defer restore() 1353 1354 created := false 1355 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1356 c.Assert(securityTag, check.Equals, "snap.snapname.hook.configure") 1357 c.Assert(opts, check.NotNil) 1358 c.Assert(opts.AllowSessionBus, check.Equals, false) 1359 created = true 1360 return nil 1361 }) 1362 defer restore() 1363 1364 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1365 panic("hooks need to create a scope and do not use systemd service tracking") 1366 }) 1367 defer restore() 1368 1369 // redirect exec 1370 execArg0 := "" 1371 execArgs := []string{} 1372 execEnv := []string{} 1373 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1374 execArg0 = arg0 1375 execArgs = args 1376 execEnv = envv 1377 return nil 1378 }) 1379 defer restore() 1380 1381 // and run it! 1382 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--hook", "configure", "-r", "x2", "snapname"}) 1383 c.Assert(err, check.IsNil) 1384 c.Assert(rest, check.DeepEquals, []string{"snapname"}) 1385 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1386 c.Check(execArgs, check.DeepEquals, []string{ 1387 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1388 "snap.snapname.hook.configure", 1389 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1390 "--hook=configure", "snapname"}) 1391 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1392 c.Assert(created, check.Equals, true) 1393 } 1394 1395 func (s *RunSuite) TestSnapRunTrackingServices(c *check.C) { 1396 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1397 defer restore() 1398 1399 // mock installed snap 1400 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1401 Revision: snap.R("x2"), 1402 }) 1403 1404 // pretend to be running from core 1405 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1406 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1407 }) 1408 defer restore() 1409 1410 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1411 panic("services rely on systemd tracking, should not have created a transient scope") 1412 }) 1413 defer restore() 1414 1415 confirmed := false 1416 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1417 confirmed = true 1418 c.Assert(securityTag, check.Equals, "snap.snapname.svc") 1419 return nil 1420 }) 1421 defer restore() 1422 1423 // redirect exec 1424 execArg0 := "" 1425 execArgs := []string{} 1426 execEnv := []string{} 1427 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1428 execArg0 = arg0 1429 execArgs = args 1430 execEnv = envv 1431 return nil 1432 }) 1433 defer restore() 1434 1435 // and run it! 1436 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.svc", "--arg1", "arg2"}) 1437 c.Assert(err, check.IsNil) 1438 c.Assert(rest, check.DeepEquals, []string{"snapname.svc", "--arg1", "arg2"}) 1439 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1440 c.Check(execArgs, check.DeepEquals, []string{ 1441 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1442 "snap.snapname.svc", 1443 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1444 "snapname.svc", "--arg1", "arg2"}) 1445 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1446 c.Assert(confirmed, check.Equals, true) 1447 } 1448 1449 func (s *RunSuite) TestSnapRunTrackingFailure(c *check.C) { 1450 restore := mockSnapConfine(filepath.Join(dirs.SnapMountDir, "core", "111", dirs.CoreLibExecDir)) 1451 defer restore() 1452 1453 // mock installed snap 1454 snaptest.MockSnapCurrent(c, string(mockYaml), &snap.SideInfo{ 1455 Revision: snap.R("x2"), 1456 }) 1457 1458 // pretend to be running from core 1459 restore = snaprun.MockOsReadlink(func(string) (string, error) { 1460 return filepath.Join(dirs.SnapMountDir, "core/111/usr/bin/snap"), nil 1461 }) 1462 defer restore() 1463 1464 created := false 1465 restore = snaprun.MockCreateTransientScopeForTracking(func(securityTag string, opts *cgroup.TrackingOptions) error { 1466 c.Assert(securityTag, check.Equals, "snap.snapname.app") 1467 c.Assert(opts, check.NotNil) 1468 c.Assert(opts.AllowSessionBus, check.Equals, true) 1469 created = true 1470 // Pretend that the tracking system was unable to track this application. 1471 return cgroup.ErrCannotTrackProcess 1472 }) 1473 defer restore() 1474 1475 restore = snaprun.MockConfirmSystemdServiceTracking(func(securityTag string) error { 1476 panic("apps need to create a scope and do not use systemd service tracking") 1477 }) 1478 defer restore() 1479 1480 // redirect exec 1481 execArg0 := "" 1482 execArgs := []string{} 1483 execEnv := []string{} 1484 restore = snaprun.MockSyscallExec(func(arg0 string, args []string, envv []string) error { 1485 execArg0 = arg0 1486 execArgs = args 1487 execEnv = envv 1488 return nil 1489 }) 1490 defer restore() 1491 1492 // Capture the debug log that is printed by this test. 1493 os.Setenv("SNAPD_DEBUG", "1") 1494 defer os.Unsetenv("SNAPD_DEBUG") 1495 logbuf, restore := logger.MockLogger() 1496 defer restore() 1497 1498 // and run it! 1499 rest, err := snaprun.Parser(snaprun.Client()).ParseArgs([]string{"run", "--", "snapname.app", "--arg1", "arg2"}) 1500 c.Assert(err, check.IsNil) 1501 c.Assert(rest, check.DeepEquals, []string{"snapname.app", "--arg1", "arg2"}) 1502 c.Check(execArg0, check.Equals, filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine")) 1503 c.Check(execArgs, check.DeepEquals, []string{ 1504 filepath.Join(dirs.SnapMountDir, "/core/111", dirs.CoreLibExecDir, "snap-confine"), 1505 "snap.snapname.app", 1506 filepath.Join(dirs.CoreLibExecDir, "snap-exec"), 1507 "snapname.app", "--arg1", "arg2"}) 1508 c.Check(execEnv, testutil.Contains, "SNAP_REVISION=x2") 1509 c.Assert(created, check.Equals, true) 1510 1511 // Ensure that the debug message is printed. 1512 c.Assert(logbuf.String(), testutil.Contains, "snapd cannot track the started application\n") 1513 }