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
5package main
6
7import (
8	"bytes"
9	"fmt"
10	"internal/testenv"
11	"io"
12	"os"
13	"syscall"
14)
15
16func gettid() int {
17	return syscall.Gettid()
18}
19
20func tidExists(tid int) (exists, supported bool, err error) {
21	// Open the magic proc status file for reading with the syscall package.
22	// We want to identify certain valid errors very precisely.
23	statusFile := fmt.Sprintf("/proc/self/task/%d/status", tid)
24	fd, err := syscall.Open(statusFile, syscall.O_RDONLY, 0)
25	if errno, ok := err.(syscall.Errno); ok {
26		if errno == syscall.ENOENT || errno == syscall.ESRCH {
27			return false, true, nil
28		}
29	}
30	if err != nil {
31		return false, false, err
32	}
33	status, err := io.ReadAll(os.NewFile(uintptr(fd), statusFile))
34	if err != nil {
35		return false, false, err
36	}
37	lines := bytes.Split(status, []byte{'\n'})
38	// Find the State line.
39	stateLineIdx := -1
40	for i, line := range lines {
41		if bytes.HasPrefix(line, []byte("State:")) {
42			stateLineIdx = i
43			break
44		}
45	}
46	if stateLineIdx < 0 {
47		// Malformed status file?
48		return false, false, fmt.Errorf("unexpected status file format: %s:\n%s", statusFile, status)
49	}
50	stateLine := bytes.SplitN(lines[stateLineIdx], []byte{':'}, 2)
51	if len(stateLine) != 2 {
52		// Malformed status file?
53		return false, false, fmt.Errorf("unexpected status file format: %s:\n%s", statusFile, status)
54	}
55	// Check if it's a zombie thread.
56	return !bytes.Contains(stateLine[1], []byte{'Z'}), true, nil
57}
58
59func getcwd() (string, error) {
60	if !syscall.ImplementsGetwd {
61		return "", nil
62	}
63	// Use the syscall to get the current working directory.
64	// This is imperative for checking for OS thread state
65	// after an unshare since os.Getwd might just check the
66	// environment, or use some other mechanism.
67	var buf [4096]byte
68	n, err := syscall.Getcwd(buf[:])
69	if err != nil {
70		return "", err
71	}
72	// Subtract one for null terminator.
73	return string(buf[:n-1]), nil
74}
75
76func unshareFs() error {
77	err := syscall.Unshare(syscall.CLONE_FS)
78	if testenv.SyscallIsNotSupported(err) {
79		return errNotPermitted
80	}
81	return err
82}
83
84func chdir(path string) error {
85	return syscall.Chdir(path)
86}
87