github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/os/exec/exec_posix_test.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build unix 6 7 package exec_test 8 9 import ( 10 "fmt" 11 "internal/testenv" 12 "io" 13 "os" 14 "os/user" 15 "path/filepath" 16 "reflect" 17 "runtime" 18 "strconv" 19 "strings" 20 "syscall" 21 "testing" 22 "time" 23 ) 24 25 func init() { 26 registerHelperCommand("pwd", cmdPwd) 27 } 28 29 func cmdPwd(...string) { 30 pwd, err := os.Getwd() 31 if err != nil { 32 fmt.Fprintln(os.Stderr, err) 33 os.Exit(1) 34 } 35 fmt.Println(pwd) 36 } 37 38 func TestCredentialNoSetGroups(t *testing.T) { 39 if runtime.GOOS == "android" { 40 maySkipHelperCommand("echo") 41 t.Skip("unsupported on Android") 42 } 43 t.Parallel() 44 45 u, err := user.Current() 46 if err != nil { 47 t.Fatalf("error getting current user: %v", err) 48 } 49 50 uid, err := strconv.Atoi(u.Uid) 51 if err != nil { 52 t.Fatalf("error converting Uid=%s to integer: %v", u.Uid, err) 53 } 54 55 gid, err := strconv.Atoi(u.Gid) 56 if err != nil { 57 t.Fatalf("error converting Gid=%s to integer: %v", u.Gid, err) 58 } 59 60 // If NoSetGroups is true, setgroups isn't called and cmd.Run should succeed 61 cmd := helperCommand(t, "echo", "foo") 62 cmd.SysProcAttr = &syscall.SysProcAttr{ 63 Credential: &syscall.Credential{ 64 Uid: uint32(uid), 65 Gid: uint32(gid), 66 NoSetGroups: true, 67 }, 68 } 69 70 if err = cmd.Run(); err != nil { 71 t.Errorf("Failed to run command: %v", err) 72 } 73 } 74 75 // For issue #19314: make sure that SIGSTOP does not cause the process 76 // to appear done. 77 func TestWaitid(t *testing.T) { 78 t.Parallel() 79 80 cmd := helperCommand(t, "pipetest") 81 stdin, err := cmd.StdinPipe() 82 if err != nil { 83 t.Fatal(err) 84 } 85 stdout, err := cmd.StdoutPipe() 86 if err != nil { 87 t.Fatal(err) 88 } 89 if err := cmd.Start(); err != nil { 90 t.Fatal(err) 91 } 92 93 // Wait for the child process to come up and register any signal handlers. 94 const msg = "O:ping\n" 95 if _, err := io.WriteString(stdin, msg); err != nil { 96 t.Fatal(err) 97 } 98 buf := make([]byte, len(msg)) 99 if _, err := io.ReadFull(stdout, buf); err != nil { 100 t.Fatal(err) 101 } 102 // Now leave the pipes open so that the process will hang until we close stdin. 103 104 if err := cmd.Process.Signal(syscall.SIGSTOP); err != nil { 105 cmd.Process.Kill() 106 t.Fatal(err) 107 } 108 109 ch := make(chan error) 110 go func() { 111 ch <- cmd.Wait() 112 }() 113 114 // Give a little time for Wait to block on waiting for the process. 115 // (This is just to give some time to trigger the bug; it should not be 116 // necessary for the test to pass.) 117 if testing.Short() { 118 time.Sleep(1 * time.Millisecond) 119 } else { 120 time.Sleep(10 * time.Millisecond) 121 } 122 123 // This call to Signal should succeed because the process still exists. 124 // (Prior to the fix for #19314, this would fail with os.ErrProcessDone 125 // or an equivalent error.) 126 if err := cmd.Process.Signal(syscall.SIGCONT); err != nil { 127 t.Error(err) 128 syscall.Kill(cmd.Process.Pid, syscall.SIGCONT) 129 } 130 131 // The SIGCONT should allow the process to wake up, notice that stdin 132 // is closed, and exit successfully. 133 stdin.Close() 134 err = <-ch 135 if err != nil { 136 t.Fatal(err) 137 } 138 } 139 140 // https://go.dev/issue/50599: if Env is not set explicitly, setting Dir should 141 // implicitly update PWD to the correct path, and Environ should list the 142 // updated value. 143 func TestImplicitPWD(t *testing.T) { 144 t.Parallel() 145 146 cwd, err := os.Getwd() 147 if err != nil { 148 t.Fatal(err) 149 } 150 151 cases := []struct { 152 name string 153 dir string 154 want string 155 }{ 156 {"empty", "", cwd}, 157 {"dot", ".", cwd}, 158 {"dotdot", "..", filepath.Dir(cwd)}, 159 {"PWD", cwd, cwd}, 160 {"PWDdotdot", cwd + string(filepath.Separator) + "..", filepath.Dir(cwd)}, 161 } 162 163 for _, tc := range cases { 164 tc := tc 165 t.Run(tc.name, func(t *testing.T) { 166 t.Parallel() 167 168 cmd := helperCommand(t, "pwd") 169 if cmd.Env != nil { 170 t.Fatalf("test requires helperCommand not to set Env field") 171 } 172 cmd.Dir = tc.dir 173 174 var pwds []string 175 for _, kv := range cmd.Environ() { 176 if strings.HasPrefix(kv, "PWD=") { 177 pwds = append(pwds, strings.TrimPrefix(kv, "PWD=")) 178 } 179 } 180 181 wantPWDs := []string{tc.want} 182 if tc.dir == "" { 183 if _, ok := os.LookupEnv("PWD"); !ok { 184 wantPWDs = nil 185 } 186 } 187 if !reflect.DeepEqual(pwds, wantPWDs) { 188 t.Errorf("PWD entries in cmd.Environ():\n\t%s\nwant:\n\t%s", strings.Join(pwds, "\n\t"), strings.Join(wantPWDs, "\n\t")) 189 } 190 191 cmd.Stderr = new(strings.Builder) 192 out, err := cmd.Output() 193 if err != nil { 194 t.Fatalf("%v:\n%s", err, cmd.Stderr) 195 } 196 got := strings.Trim(string(out), "\r\n") 197 t.Logf("in\n\t%s\n`pwd` reported\n\t%s", tc.dir, got) 198 if got != tc.want { 199 t.Errorf("want\n\t%s", tc.want) 200 } 201 }) 202 } 203 } 204 205 // However, if cmd.Env is set explicitly, setting Dir should not override it. 206 // (This checks that the implementation for https://go.dev/issue/50599 doesn't 207 // break existing users who may have explicitly mismatched the PWD variable.) 208 func TestExplicitPWD(t *testing.T) { 209 t.Parallel() 210 211 maySkipHelperCommand("pwd") 212 testenv.MustHaveSymlink(t) 213 214 cwd, err := os.Getwd() 215 if err != nil { 216 t.Fatal(err) 217 } 218 219 link := filepath.Join(t.TempDir(), "link") 220 if err := os.Symlink(cwd, link); err != nil { 221 t.Fatal(err) 222 } 223 224 // Now link is another equally-valid name for cwd. If we set Dir to one and 225 // PWD to the other, the subprocess should report the PWD version. 226 cases := []struct { 227 name string 228 dir string 229 pwd string 230 }{ 231 {name: "original PWD", pwd: cwd}, 232 {name: "link PWD", pwd: link}, 233 {name: "in link with original PWD", dir: link, pwd: cwd}, 234 {name: "in dir with link PWD", dir: cwd, pwd: link}, 235 // Ideally we would also like to test what happens if we set PWD to 236 // something totally bogus (or the empty string), but then we would have no 237 // idea what output the subprocess should actually produce: cwd itself may 238 // contain symlinks preserved from the PWD value in the test's environment. 239 } 240 for _, tc := range cases { 241 tc := tc 242 t.Run(tc.name, func(t *testing.T) { 243 t.Parallel() 244 245 cmd := helperCommand(t, "pwd") 246 // This is intentionally opposite to the usual order of setting cmd.Dir 247 // and then calling cmd.Environ. Here, we *want* PWD not to match cmd.Dir, 248 // so we don't care whether cmd.Dir is reflected in cmd.Environ. 249 cmd.Env = append(cmd.Environ(), "PWD="+tc.pwd) 250 cmd.Dir = tc.dir 251 252 var pwds []string 253 for _, kv := range cmd.Environ() { 254 if strings.HasPrefix(kv, "PWD=") { 255 pwds = append(pwds, strings.TrimPrefix(kv, "PWD=")) 256 } 257 } 258 259 wantPWDs := []string{tc.pwd} 260 if !reflect.DeepEqual(pwds, wantPWDs) { 261 t.Errorf("PWD entries in cmd.Environ():\n\t%s\nwant:\n\t%s", strings.Join(pwds, "\n\t"), strings.Join(wantPWDs, "\n\t")) 262 } 263 264 cmd.Stderr = new(strings.Builder) 265 out, err := cmd.Output() 266 if err != nil { 267 t.Fatalf("%v:\n%s", err, cmd.Stderr) 268 } 269 got := strings.Trim(string(out), "\r\n") 270 t.Logf("in\n\t%s\nwith PWD=%s\nsubprocess os.Getwd() reported\n\t%s", tc.dir, tc.pwd, got) 271 if got != tc.pwd { 272 t.Errorf("want\n\t%s", tc.pwd) 273 } 274 }) 275 } 276 }