github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/snap-exec/main_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 "fmt" 24 "io/ioutil" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "testing" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/osutil" 34 "github.com/snapcore/snapd/release" 35 "github.com/snapcore/snapd/snap" 36 "github.com/snapcore/snapd/snap/snaptest" 37 "github.com/snapcore/snapd/testutil" 38 39 snapExec "github.com/snapcore/snapd/cmd/snap-exec" 40 ) 41 42 // Hook up check.v1 into the "go test" runner 43 func Test(t *testing.T) { TestingT(t) } 44 45 type snapExecSuite struct{} 46 47 var _ = Suite(&snapExecSuite{}) 48 49 func (s *snapExecSuite) SetUpTest(c *C) { 50 // clean previous parse runs 51 snapExec.SetOptsCommand("") 52 snapExec.SetOptsHook("") 53 } 54 55 func (s *snapExecSuite) TearDown(c *C) { 56 dirs.SetRootDir("/") 57 } 58 59 var mockYaml = []byte(`name: snapname 60 version: 1.0 61 apps: 62 app: 63 command: run-app cmd-arg1 $SNAP_DATA 64 stop-command: stop-app 65 post-stop-command: post-stop-app 66 completer: you/complete/me 67 environment: 68 BASE_PATH: /some/path 69 LD_LIBRARY_PATH: ${BASE_PATH}/lib 70 MY_PATH: $PATH 71 TEST_PATH: /custom 72 app2: 73 command: run-app2 74 stop-command: stop-app2 75 post-stop-command: post-stop-app2 76 command-chain: [chain1, chain2] 77 nostop: 78 command: nostop 79 `) 80 81 var mockClassicYaml = append([]byte("confinement: classic\n"), mockYaml...) 82 83 var mockHookYaml = []byte(`name: snapname 84 version: 1.0 85 hooks: 86 configure: 87 `) 88 89 var mockHookCommandChainYaml = []byte(`name: snapname 90 version: 1.0 91 hooks: 92 configure: 93 command-chain: [chain1, chain2] 94 `) 95 96 var binaryTemplate = `#!/bin/sh 97 echo "$(basename $0)" >> %[1]q 98 for arg in "$@"; do 99 echo "$arg" >> %[1]q 100 done 101 printf "\n" >> %[1]q` 102 103 func (s *snapExecSuite) TestInvalidCombinedParameters(c *C) { 104 invalidParameters := []string{"--hook=hook-name", "--command=command-name", "snap-name"} 105 _, _, err := snapExec.ParseArgs(invalidParameters) 106 c.Check(err, ErrorMatches, ".*cannot use --hook and --command together.*") 107 } 108 109 func (s *snapExecSuite) TestInvalidExtraParameters(c *C) { 110 invalidParameters := []string{"--hook=hook-name", "snap-name", "foo", "bar"} 111 _, _, err := snapExec.ParseArgs(invalidParameters) 112 c.Check(err, ErrorMatches, ".*too many arguments for hook \"hook-name\": snap-name foo bar.*") 113 } 114 115 func (s *snapExecSuite) TestFindCommand(c *C) { 116 info, err := snap.InfoFromSnapYaml(mockYaml) 117 c.Assert(err, IsNil) 118 119 for _, t := range []struct { 120 cmd string 121 expected string 122 }{ 123 {cmd: "", expected: `run-app cmd-arg1 $SNAP_DATA`}, 124 {cmd: "stop", expected: "stop-app"}, 125 {cmd: "post-stop", expected: "post-stop-app"}, 126 } { 127 cmd, err := snapExec.FindCommand(info.Apps["app"], t.cmd) 128 c.Check(err, IsNil) 129 c.Check(cmd, Equals, t.expected) 130 } 131 } 132 133 func (s *snapExecSuite) TestFindCommandInvalidCommand(c *C) { 134 info, err := snap.InfoFromSnapYaml(mockYaml) 135 c.Assert(err, IsNil) 136 137 _, err = snapExec.FindCommand(info.Apps["app"], "xxx") 138 c.Check(err, ErrorMatches, `cannot use "xxx" command`) 139 } 140 141 func (s *snapExecSuite) TestFindCommandNoCommand(c *C) { 142 info, err := snap.InfoFromSnapYaml(mockYaml) 143 c.Assert(err, IsNil) 144 145 _, err = snapExec.FindCommand(info.Apps["nostop"], "stop") 146 c.Check(err, ErrorMatches, `no "stop" command found for "nostop"`) 147 } 148 149 func (s *snapExecSuite) TestSnapExecAppIntegration(c *C) { 150 dirs.SetRootDir(c.MkDir()) 151 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 152 Revision: snap.R("42"), 153 }) 154 155 execArgv0 := "" 156 execArgs := []string{} 157 execEnv := []string{} 158 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 159 execArgv0 = argv0 160 execArgs = argv 161 execEnv = env 162 return nil 163 }) 164 defer restore() 165 166 // FIXME: TEST_PATH was meant to be just PATH but this uncovers another 167 // bug in the test suite where mocking binaries misbehaves. 168 oldPath := os.Getenv("TEST_PATH") 169 os.Setenv("TEST_PATH", "/vanilla") 170 defer os.Setenv("TEST_PATH", oldPath) 171 172 // launch and verify its run the right way 173 err := snapExec.ExecApp("snapname.app", "42", "stop", []string{"arg1", "arg2"}) 174 c.Assert(err, IsNil) 175 c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/stop-app", dirs.SnapMountDir)) 176 c.Check(execArgs, DeepEquals, []string{execArgv0, "arg1", "arg2"}) 177 c.Check(execEnv, testutil.Contains, "BASE_PATH=/some/path") 178 c.Check(execEnv, testutil.Contains, "LD_LIBRARY_PATH=/some/path/lib") 179 c.Check(execEnv, testutil.Contains, fmt.Sprintf("MY_PATH=%s", os.Getenv("PATH"))) 180 // TEST_PATH is properly handled and we only see one value, /custom, defined 181 // as an app-specific override. 182 // See also https://bugs.launchpad.net/snapd/+bug/1860369 183 c.Check(execEnv, Not(testutil.Contains), "TEST_PATH=/vanilla") 184 c.Check(execEnv, testutil.Contains, "TEST_PATH=/custom") 185 } 186 187 func (s *snapExecSuite) TestSnapExecAppCommandChainIntegration(c *C) { 188 dirs.SetRootDir(c.MkDir()) 189 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 190 Revision: snap.R("42"), 191 }) 192 193 execArgv0 := "" 194 execArgs := []string{} 195 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 196 execArgv0 = argv0 197 execArgs = argv 198 return nil 199 }) 200 defer restore() 201 202 chain1_path := fmt.Sprintf("%s/snapname/42/chain1", dirs.SnapMountDir) 203 chain2_path := fmt.Sprintf("%s/snapname/42/chain2", dirs.SnapMountDir) 204 app_path := fmt.Sprintf("%s/snapname/42/run-app2", dirs.SnapMountDir) 205 stop_path := fmt.Sprintf("%s/snapname/42/stop-app2", dirs.SnapMountDir) 206 post_stop_path := fmt.Sprintf("%s/snapname/42/post-stop-app2", dirs.SnapMountDir) 207 208 for _, t := range []struct { 209 cmd string 210 args []string 211 expected []string 212 }{ 213 // Normal command 214 {expected: []string{chain1_path, chain2_path, app_path}}, 215 {args: []string{"arg1", "arg2"}, expected: []string{chain1_path, chain2_path, app_path, "arg1", "arg2"}}, 216 217 // Stop command 218 {cmd: "stop", expected: []string{chain1_path, chain2_path, stop_path}}, 219 {cmd: "stop", args: []string{"arg1", "arg2"}, expected: []string{chain1_path, chain2_path, stop_path, "arg1", "arg2"}}, 220 221 // Post-stop command 222 {cmd: "post-stop", expected: []string{chain1_path, chain2_path, post_stop_path}}, 223 {cmd: "post-stop", args: []string{"arg1", "arg2"}, expected: []string{chain1_path, chain2_path, post_stop_path, "arg1", "arg2"}}, 224 } { 225 err := snapExec.ExecApp("snapname.app2", "42", t.cmd, t.args) 226 c.Assert(err, IsNil) 227 c.Check(execArgv0, Equals, t.expected[0]) 228 c.Check(execArgs, DeepEquals, t.expected) 229 } 230 } 231 232 func (s *snapExecSuite) TestSnapExecHookIntegration(c *C) { 233 dirs.SetRootDir(c.MkDir()) 234 snaptest.MockSnap(c, string(mockHookYaml), &snap.SideInfo{ 235 Revision: snap.R("42"), 236 }) 237 238 execArgv0 := "" 239 execArgs := []string{} 240 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 241 execArgv0 = argv0 242 execArgs = argv 243 return nil 244 }) 245 defer restore() 246 247 // launch and verify it ran correctly 248 err := snapExec.ExecHook("snapname", "42", "configure") 249 c.Assert(err, IsNil) 250 c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/meta/hooks/configure", dirs.SnapMountDir)) 251 c.Check(execArgs, DeepEquals, []string{execArgv0}) 252 } 253 254 func (s *snapExecSuite) TestSnapExecHookCommandChainIntegration(c *C) { 255 dirs.SetRootDir(c.MkDir()) 256 snaptest.MockSnap(c, string(mockHookCommandChainYaml), &snap.SideInfo{ 257 Revision: snap.R("42"), 258 }) 259 260 execArgv0 := "" 261 execArgs := []string{} 262 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 263 execArgv0 = argv0 264 execArgs = argv 265 return nil 266 }) 267 defer restore() 268 269 chain1_path := fmt.Sprintf("%s/snapname/42/chain1", dirs.SnapMountDir) 270 chain2_path := fmt.Sprintf("%s/snapname/42/chain2", dirs.SnapMountDir) 271 hook_path := fmt.Sprintf("%s/snapname/42/meta/hooks/configure", dirs.SnapMountDir) 272 273 err := snapExec.ExecHook("snapname", "42", "configure") 274 c.Assert(err, IsNil) 275 c.Check(execArgv0, Equals, chain1_path) 276 c.Check(execArgs, DeepEquals, []string{chain1_path, chain2_path, hook_path}) 277 } 278 279 func (s *snapExecSuite) TestSnapExecHookMissingHookIntegration(c *C) { 280 dirs.SetRootDir(c.MkDir()) 281 snaptest.MockSnap(c, string(mockHookYaml), &snap.SideInfo{ 282 Revision: snap.R("42"), 283 }) 284 285 err := snapExec.ExecHook("snapname", "42", "missing-hook") 286 c.Assert(err, NotNil) 287 c.Assert(err, ErrorMatches, "cannot find hook \"missing-hook\" in \"snapname\"") 288 } 289 290 func (s *snapExecSuite) TestSnapExecIgnoresUnknownArgs(c *C) { 291 snapApp, rest, err := snapExec.ParseArgs([]string{"--command=shell", "snapname.app", "--arg1", "arg2"}) 292 c.Assert(err, IsNil) 293 c.Assert(snapExec.GetOptsCommand(), Equals, "shell") 294 c.Assert(snapApp, DeepEquals, "snapname.app") 295 c.Assert(rest, DeepEquals, []string{"--arg1", "arg2"}) 296 } 297 298 func (s *snapExecSuite) TestSnapExecErrorsOnUnknown(c *C) { 299 _, _, err := snapExec.ParseArgs([]string{"--command=shell", "--unknown", "snapname.app", "--arg1", "arg2"}) 300 c.Check(err, ErrorMatches, "unknown flag `unknown'") 301 } 302 303 func (s *snapExecSuite) TestSnapExecErrorsOnMissingSnapApp(c *C) { 304 _, _, err := snapExec.ParseArgs([]string{"--command=shell"}) 305 c.Check(err, ErrorMatches, "need the application to run as argument") 306 } 307 308 func (s *snapExecSuite) TestSnapExecAppRealIntegration(c *C) { 309 // we need a lot of mocks 310 dirs.SetRootDir(c.MkDir()) 311 312 oldOsArgs := os.Args 313 defer func() { os.Args = oldOsArgs }() 314 315 os.Setenv("SNAP_REVISION", "42") 316 defer os.Unsetenv("SNAP_REVISION") 317 318 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 319 Revision: snap.R("42"), 320 }) 321 322 canaryFile := filepath.Join(c.MkDir(), "canary.txt") 323 script := fmt.Sprintf("%s/snapname/42/run-app", dirs.SnapMountDir) 324 err := ioutil.WriteFile(script, []byte(fmt.Sprintf(binaryTemplate, canaryFile)), 0755) 325 c.Assert(err, IsNil) 326 327 // we can not use the real syscall.execv here because it would 328 // replace the entire test :) 329 restore := snapExec.MockSyscallExec(actuallyExec) 330 defer restore() 331 332 // run it 333 os.Args = []string{"snap-exec", "snapname.app", "foo", "--bar=baz", "foobar"} 334 err = snapExec.Run() 335 c.Assert(err, IsNil) 336 337 c.Assert(canaryFile, testutil.FileEquals, `run-app 338 cmd-arg1 339 foo 340 --bar=baz 341 foobar 342 343 `) 344 } 345 346 func (s *snapExecSuite) TestSnapExecHookRealIntegration(c *C) { 347 // we need a lot of mocks 348 dirs.SetRootDir(c.MkDir()) 349 350 oldOsArgs := os.Args 351 defer func() { os.Args = oldOsArgs }() 352 353 os.Setenv("SNAP_REVISION", "42") 354 defer os.Unsetenv("SNAP_REVISION") 355 356 canaryFile := filepath.Join(c.MkDir(), "canary.txt") 357 358 testSnap := snaptest.MockSnap(c, string(mockHookYaml), &snap.SideInfo{ 359 Revision: snap.R("42"), 360 }) 361 hookPath := filepath.Join("meta", "hooks", "configure") 362 hookPathAndContents := []string{hookPath, fmt.Sprintf(binaryTemplate, canaryFile)} 363 snaptest.PopulateDir(testSnap.MountDir(), [][]string{hookPathAndContents}) 364 hookPath = filepath.Join(testSnap.MountDir(), hookPath) 365 c.Assert(os.Chmod(hookPath, 0755), IsNil) 366 367 // we can not use the real syscall.execv here because it would 368 // replace the entire test :) 369 restore := snapExec.MockSyscallExec(actuallyExec) 370 defer restore() 371 372 // run it 373 os.Args = []string{"snap-exec", "--hook=configure", "snapname"} 374 err := snapExec.Run() 375 c.Assert(err, IsNil) 376 377 c.Assert(canaryFile, testutil.FileEquals, "configure\n\n") 378 } 379 380 func actuallyExec(argv0 string, argv []string, env []string) error { 381 cmd := exec.Command(argv[0], argv[1:]...) 382 cmd.Env = env 383 output, err := cmd.CombinedOutput() 384 if len(output) > 0 { 385 return fmt.Errorf("Expected output length to be 0, it was %d", len(output)) 386 } 387 return err 388 } 389 390 func (s *snapExecSuite) TestSnapExecShellIntegration(c *C) { 391 dirs.SetRootDir(c.MkDir()) 392 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 393 Revision: snap.R("42"), 394 }) 395 396 execArgv0 := "" 397 execArgs := []string{} 398 execEnv := []string{} 399 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 400 execArgv0 = argv0 401 execArgs = argv 402 execEnv = env 403 return nil 404 }) 405 defer restore() 406 407 // launch and verify its run the right way 408 err := snapExec.ExecApp("snapname.app", "42", "shell", []string{"-c", "echo foo"}) 409 c.Assert(err, IsNil) 410 c.Check(execArgv0, Equals, "/bin/bash") 411 c.Check(execArgs, DeepEquals, []string{execArgv0, "-c", "echo foo"}) 412 c.Check(execEnv, testutil.Contains, "LD_LIBRARY_PATH=/some/path/lib") 413 414 // launch and verify shell still runs the command chain 415 err = snapExec.ExecApp("snapname.app2", "42", "shell", []string{"-c", "echo foo"}) 416 c.Assert(err, IsNil) 417 chain1 := fmt.Sprintf("%s/snapname/42/chain1", dirs.SnapMountDir) 418 chain2 := fmt.Sprintf("%s/snapname/42/chain2", dirs.SnapMountDir) 419 c.Check(execArgv0, Equals, chain1) 420 c.Check(execArgs, DeepEquals, []string{chain1, chain2, "/bin/bash", "-c", "echo foo"}) 421 } 422 423 func (s *snapExecSuite) TestSnapExecAppIntegrationWithVars(c *C) { 424 dirs.SetRootDir(c.MkDir()) 425 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 426 Revision: snap.R("42"), 427 }) 428 429 execArgv0 := "" 430 execArgs := []string{} 431 execEnv := []string{} 432 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 433 execArgv0 = argv0 434 execArgs = argv 435 execEnv = env 436 return nil 437 }) 438 defer restore() 439 440 // setup env 441 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 442 defer os.Unsetenv("SNAP_DATA") 443 444 // launch and verify its run the right way 445 err := snapExec.ExecApp("snapname.app", "42", "", []string{"user-arg1"}) 446 c.Assert(err, IsNil) 447 c.Check(execArgv0, Equals, fmt.Sprintf("%s/snapname/42/run-app", dirs.SnapMountDir)) 448 c.Check(execArgs, DeepEquals, []string{execArgv0, "cmd-arg1", "/var/snap/snapname/42", "user-arg1"}) 449 c.Check(execEnv, testutil.Contains, "BASE_PATH=/some/path") 450 c.Check(execEnv, testutil.Contains, "LD_LIBRARY_PATH=/some/path/lib") 451 c.Check(execEnv, testutil.Contains, fmt.Sprintf("MY_PATH=%s", os.Getenv("PATH"))) 452 } 453 454 func (s *snapExecSuite) TestSnapExecExpandEnvCmdArgs(c *C) { 455 for _, t := range []struct { 456 args []string 457 env map[string]string 458 expected []string 459 }{ 460 { 461 args: []string{"foo"}, 462 env: nil, 463 expected: []string{"foo"}, 464 }, 465 { 466 args: []string{"$var"}, 467 env: map[string]string{"var": "value"}, 468 expected: []string{"value"}, 469 }, 470 { 471 args: []string{"foo", "$not_existing"}, 472 env: nil, 473 expected: []string{"foo"}, 474 }, 475 { 476 args: []string{"foo", "$var", "baz"}, 477 env: map[string]string{"var": "bar", "unrelated": "env"}, 478 expected: []string{"foo", "bar", "baz"}, 479 }, 480 } { 481 env := osutil.Environment(t.env) 482 c.Check(snapExec.ExpandEnvCmdArgs(t.args, env), DeepEquals, t.expected) 483 } 484 } 485 486 func (s *snapExecSuite) TestSnapExecCompleteError(c *C) { 487 dirs.SetRootDir(c.MkDir()) 488 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 489 Revision: snap.R("42"), 490 }) 491 492 restore := snapExec.MockOsReadlink(func(p string) (string, error) { 493 c.Assert(p, Equals, "/proc/self/exe") 494 return "", fmt.Errorf("fail") 495 }) 496 defer restore() 497 498 // setup env 499 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 500 defer os.Unsetenv("SNAP_DATA") 501 502 // launch and verify its run the right way 503 err := snapExec.ExecApp("snapname.app", "42", "complete", []string{"foo"}) 504 c.Assert(err, ErrorMatches, "cannot find completion helper: fail") 505 } 506 507 func (s *snapExecSuite) TestSnapExecCompleteConfined(c *C) { 508 dirs.SetRootDir(c.MkDir()) 509 snaptest.MockSnap(c, string(mockYaml), &snap.SideInfo{ 510 Revision: snap.R("42"), 511 }) 512 513 execArgv0 := "" 514 execArgs := []string{} 515 restore := snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 516 execArgv0 = argv0 517 execArgs = argv 518 return nil 519 }) 520 defer restore() 521 522 restore = snapExec.MockOsReadlink(func(p string) (string, error) { 523 c.Assert(p, Equals, "/proc/self/exe") 524 // as if running inside the snap mount namespace 525 return "/usr/lib/snapd/snap-exec", nil 526 }) 527 defer restore() 528 529 // setup env 530 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 531 defer os.Unsetenv("SNAP_DATA") 532 533 // launch and verify its run the right way 534 err := snapExec.ExecApp("snapname.app", "42", "complete", []string{"foo"}) 535 c.Assert(err, IsNil) 536 c.Check(execArgv0, Equals, "/bin/bash") 537 c.Check(execArgs, DeepEquals, []string{execArgv0, 538 dirs.CompletionHelperInCore, 539 filepath.Join(dirs.SnapMountDir, "snapname/42/you/complete/me"), 540 "foo"}) 541 } 542 543 func (s *snapExecSuite) TestSnapExecCompleteClassicReexec(c *C) { 544 restore := release.MockReleaseInfo(&release.OS{ID: "ubuntu"}) 545 defer restore() 546 dirs.SetRootDir(c.MkDir()) 547 snaptest.MockSnap(c, string(mockClassicYaml), &snap.SideInfo{ 548 Revision: snap.R("42"), 549 }) 550 551 execArgv0 := "" 552 execArgs := []string{} 553 restore = snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 554 execArgv0 = argv0 555 execArgs = argv 556 return nil 557 }) 558 defer restore() 559 560 restore = snapExec.MockOsReadlink(func(p string) (string, error) { 561 c.Assert(p, Equals, "/proc/self/exe") 562 // as if it's reexeced from the snap 563 return filepath.Join(dirs.SnapMountDir, "core/current", dirs.CoreLibExecDir, "snap-exec"), nil 564 }) 565 defer restore() 566 567 // setup env 568 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 569 defer os.Unsetenv("SNAP_DATA") 570 571 // launch and verify its run the right way 572 err := snapExec.ExecApp("snapname.app", "42", "complete", []string{"foo"}) 573 c.Assert(err, IsNil) 574 c.Check(execArgv0, Equals, "/bin/bash") 575 c.Check(execArgs, DeepEquals, []string{execArgv0, 576 filepath.Join(dirs.SnapMountDir, "core/current", dirs.CompletionHelperInCore), 577 filepath.Join(dirs.SnapMountDir, "snapname/42/you/complete/me"), 578 "foo"}) 579 } 580 581 func (s *snapExecSuite) TestSnapExecCompleteClassicNoReexec(c *C) { 582 restore := release.MockReleaseInfo(&release.OS{ID: "centos"}) 583 defer restore() 584 dirs.SetRootDir(c.MkDir()) 585 snaptest.MockSnap(c, string(mockClassicYaml), &snap.SideInfo{ 586 Revision: snap.R("42"), 587 }) 588 589 execArgv0 := "" 590 execArgs := []string{} 591 execEnv := []string{} 592 restore = snapExec.MockSyscallExec(func(argv0 string, argv []string, env []string) error { 593 execArgv0 = argv0 594 execArgs = argv 595 execEnv = env 596 return nil 597 }) 598 defer restore() 599 600 restore = snapExec.MockOsReadlink(func(p string) (string, error) { 601 c.Assert(p, Equals, "/proc/self/exe") 602 // running from distro libexecdir 603 return filepath.Join(dirs.DistroLibExecDir, "snap-exec"), nil 604 }) 605 defer restore() 606 607 // setup env 608 os.Setenv("SNAP_DATA", "/var/snap/snapname/42") 609 defer os.Unsetenv("SNAP_DATA") 610 os.Setenv("SNAP_SAVED_TMPDIR", "/var/tmp99") 611 defer os.Unsetenv("SNAP_SAVED_TMPDIR") 612 613 // launch and verify its run the right way 614 err := snapExec.ExecApp("snapname.app", "42", "complete", []string{"foo"}) 615 c.Assert(err, IsNil) 616 c.Check(execArgv0, Equals, "/bin/bash") 617 c.Check(execArgs, DeepEquals, []string{execArgv0, 618 filepath.Join(dirs.DistroLibExecDir, "etelpmoc.sh"), 619 filepath.Join(dirs.SnapMountDir, "snapname/42/you/complete/me"), 620 "foo"}) 621 c.Check(execEnv, testutil.Contains, "SNAP_DATA=/var/snap/snapname/42") 622 c.Check(execEnv, testutil.Contains, "TMPDIR=/var/tmp99") 623 }