github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/toolkit/process/process_openbsd.go (about)

     1  //go:build openbsd
     2  // +build openbsd
     3  
     4  package process
     5  
     6  import (
     7  	"bytes"
     8  	"context"
     9  	"encoding/binary"
    10  	"fmt"
    11  	"io"
    12  	"path/filepath"
    13  	"strconv"
    14  	"strings"
    15  	"unsafe"
    16  
    17  	cpu "github.com/unionj-cloud/go-doudou/toolkit/cpu"
    18  	"github.com/unionj-cloud/go-doudou/toolkit/internal/common"
    19  	mem "github.com/unionj-cloud/go-doudou/toolkit/mem"
    20  	net "github.com/unionj-cloud/go-doudou/toolkit/net"
    21  	"golang.org/x/sys/unix"
    22  )
    23  
    24  func pidsWithContext(ctx context.Context) ([]int32, error) {
    25  	var ret []int32
    26  	procs, err := ProcessesWithContext(ctx)
    27  	if err != nil {
    28  		return ret, nil
    29  	}
    30  
    31  	for _, p := range procs {
    32  		ret = append(ret, p.Pid)
    33  	}
    34  
    35  	return ret, nil
    36  }
    37  
    38  func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
    39  	k, err := p.getKProc()
    40  	if err != nil {
    41  		return 0, err
    42  	}
    43  
    44  	return k.Ppid, nil
    45  }
    46  
    47  func (p *Process) NameWithContext(ctx context.Context) (string, error) {
    48  	k, err := p.getKProc()
    49  	if err != nil {
    50  		return "", err
    51  	}
    52  	name := common.IntToString(k.Comm[:])
    53  
    54  	if len(name) >= 15 {
    55  		cmdlineSlice, err := p.CmdlineSliceWithContext(ctx)
    56  		if err != nil {
    57  			return "", err
    58  		}
    59  		if len(cmdlineSlice) > 0 {
    60  			extendedName := filepath.Base(cmdlineSlice[0])
    61  			if strings.HasPrefix(extendedName, p.name) {
    62  				name = extendedName
    63  			} else {
    64  				name = cmdlineSlice[0]
    65  			}
    66  		}
    67  	}
    68  
    69  	return name, nil
    70  }
    71  
    72  func (p *Process) CwdWithContext(ctx context.Context) (string, error) {
    73  	return "", common.ErrNotImplementedError
    74  }
    75  
    76  func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
    77  	return "", common.ErrNotImplementedError
    78  }
    79  
    80  func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
    81  	mib := []int32{CTLKern, KernProcArgs, p.Pid, KernProcArgv}
    82  	buf, _, err := common.CallSyscall(mib)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	/* From man sysctl(2):
    88  	The buffer pointed to by oldp is filled with an array of char
    89  	pointers followed by the strings themselves. The last char
    90  	pointer is a NULL pointer. */
    91  	var strParts []string
    92  	r := bytes.NewReader(buf)
    93  	baseAddr := uintptr(unsafe.Pointer(&buf[0]))
    94  	for {
    95  		argvp, err := readPtr(r)
    96  		if err != nil {
    97  			return nil, err
    98  		}
    99  		if argvp == 0 { // check for a NULL pointer
   100  			break
   101  		}
   102  		offset := argvp - baseAddr
   103  		length := uintptr(bytes.IndexByte(buf[offset:], 0))
   104  		str := string(buf[offset : offset+length])
   105  		strParts = append(strParts, str)
   106  	}
   107  
   108  	return strParts, nil
   109  }
   110  
   111  // readPtr reads a pointer data from a given reader. WARNING: only little
   112  // endian architectures are supported.
   113  func readPtr(r io.Reader) (uintptr, error) {
   114  	switch sizeofPtr {
   115  	case 4:
   116  		var p uint32
   117  		if err := binary.Read(r, binary.LittleEndian, &p); err != nil {
   118  			return 0, err
   119  		}
   120  		return uintptr(p), nil
   121  	case 8:
   122  		var p uint64
   123  		if err := binary.Read(r, binary.LittleEndian, &p); err != nil {
   124  			return 0, err
   125  		}
   126  		return uintptr(p), nil
   127  	default:
   128  		return 0, fmt.Errorf("unsupported pointer size")
   129  	}
   130  }
   131  
   132  func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) {
   133  	argv, err := p.CmdlineSliceWithContext(ctx)
   134  	if err != nil {
   135  		return "", err
   136  	}
   137  	return strings.Join(argv, " "), nil
   138  }
   139  
   140  func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
   141  	return 0, common.ErrNotImplementedError
   142  }
   143  
   144  func (p *Process) StatusWithContext(ctx context.Context) ([]string, error) {
   145  	k, err := p.getKProc()
   146  	if err != nil {
   147  		return []string{""}, err
   148  	}
   149  	var s string
   150  	switch k.Stat {
   151  	case SIDL:
   152  	case SRUN:
   153  	case SONPROC:
   154  		s = Running
   155  	case SSLEEP:
   156  		s = Sleep
   157  	case SSTOP:
   158  		s = Stop
   159  	case SDEAD:
   160  		s = Zombie
   161  	}
   162  
   163  	return []string{s}, nil
   164  }
   165  
   166  func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
   167  	// see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details
   168  	pid := p.Pid
   169  	out, err := invoke.CommandWithContext(ctx, "ps", "-o", "stat=", "-p", strconv.Itoa(int(pid)))
   170  	if err != nil {
   171  		return false, err
   172  	}
   173  	return strings.IndexByte(string(out), '+') != -1, nil
   174  }
   175  
   176  func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
   177  	k, err := p.getKProc()
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	uids := make([]int32, 0, 3)
   183  
   184  	uids = append(uids, int32(k.Ruid), int32(k.Uid), int32(k.Svuid))
   185  
   186  	return uids, nil
   187  }
   188  
   189  func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
   190  	k, err := p.getKProc()
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	gids := make([]int32, 0, 3)
   196  	gids = append(gids, int32(k.Rgid), int32(k.Ngroups), int32(k.Svgid))
   197  
   198  	return gids, nil
   199  }
   200  
   201  func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
   202  	k, err := p.getKProc()
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	groups := make([]int32, k.Ngroups)
   208  	for i := int16(0); i < k.Ngroups; i++ {
   209  		groups[i] = int32(k.Groups[i])
   210  	}
   211  
   212  	return groups, nil
   213  }
   214  
   215  func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
   216  	k, err := p.getKProc()
   217  	if err != nil {
   218  		return "", err
   219  	}
   220  
   221  	ttyNr := uint64(k.Tdev)
   222  
   223  	termmap, err := getTerminalMap()
   224  	if err != nil {
   225  		return "", err
   226  	}
   227  
   228  	return termmap[ttyNr], nil
   229  }
   230  
   231  func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
   232  	k, err := p.getKProc()
   233  	if err != nil {
   234  		return 0, err
   235  	}
   236  	return int32(k.Nice), nil
   237  }
   238  
   239  func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
   240  	k, err := p.getKProc()
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	return &IOCountersStat{
   245  		ReadCount:  uint64(k.Uru_inblock),
   246  		WriteCount: uint64(k.Uru_oublock),
   247  	}, nil
   248  }
   249  
   250  func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
   251  	/* not supported, just return 1 */
   252  	return 1, nil
   253  }
   254  
   255  func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
   256  	k, err := p.getKProc()
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  	return &cpu.TimesStat{
   261  		CPU:    "cpu",
   262  		User:   float64(k.Uutime_sec) + float64(k.Uutime_usec)/1000000,
   263  		System: float64(k.Ustime_sec) + float64(k.Ustime_usec)/1000000,
   264  	}, nil
   265  }
   266  
   267  func (p *Process) times1() (cpu.TimesStat, error) {
   268  	k, err := p.getKProc()
   269  	if err != nil {
   270  		return cpu.TimesStat{}, err
   271  	}
   272  	return cpu.TimesStat{
   273  		CPU:    "cpu",
   274  		User:   float64(k.Uutime_sec) + float64(k.Uutime_usec)/1000000,
   275  		System: float64(k.Ustime_sec) + float64(k.Ustime_usec)/1000000,
   276  	}, nil
   277  }
   278  
   279  func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
   280  	k, err := p.getKProc()
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  	pageSize, err := mem.GetPageSizeWithContext(ctx)
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  
   289  	return &MemoryInfoStat{
   290  		RSS: uint64(k.Vm_rssize) * pageSize,
   291  		VMS: uint64(k.Vm_tsize) + uint64(k.Vm_dsize) +
   292  			uint64(k.Vm_ssize),
   293  	}, nil
   294  }
   295  
   296  func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
   297  	pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid)
   298  	if err != nil {
   299  		return nil, err
   300  	}
   301  	ret := make([]*Process, 0, len(pids))
   302  	for _, pid := range pids {
   303  		np, err := NewProcessWithContext(ctx, pid)
   304  		if err != nil {
   305  			return nil, err
   306  		}
   307  		ret = append(ret, np)
   308  	}
   309  	return ret, nil
   310  }
   311  
   312  func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
   313  	return nil, common.ErrNotImplementedError
   314  }
   315  
   316  func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
   317  	return nil, common.ErrNotImplementedError
   318  }
   319  
   320  func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
   321  	results := []*Process{}
   322  
   323  	buf, length, err := callKernProcSyscall(KernProcAll, 0)
   324  	if err != nil {
   325  		return results, err
   326  	}
   327  
   328  	// get kinfo_proc size
   329  	count := int(length / uint64(sizeOfKinfoProc))
   330  
   331  	// parse buf to procs
   332  	for i := 0; i < count; i++ {
   333  		b := buf[i*sizeOfKinfoProc : (i+1)*sizeOfKinfoProc]
   334  		k, err := parseKinfoProc(b)
   335  		if err != nil {
   336  			continue
   337  		}
   338  		p, err := NewProcessWithContext(ctx, int32(k.Pid))
   339  		if err != nil {
   340  			continue
   341  		}
   342  
   343  		results = append(results, p)
   344  	}
   345  
   346  	return results, nil
   347  }
   348  
   349  func (p *Process) getKProc() (*KinfoProc, error) {
   350  	buf, length, err := callKernProcSyscall(KernProcPID, p.Pid)
   351  	if err != nil {
   352  		return nil, err
   353  	}
   354  	if length != sizeOfKinfoProc {
   355  		return nil, err
   356  	}
   357  
   358  	k, err := parseKinfoProc(buf)
   359  	if err != nil {
   360  		return nil, err
   361  	}
   362  	return &k, nil
   363  }
   364  
   365  func callKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) {
   366  	mib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0}
   367  	mibptr := unsafe.Pointer(&mib[0])
   368  	miblen := uint64(len(mib))
   369  	length := uint64(0)
   370  	_, _, err := unix.Syscall6(
   371  		unix.SYS___SYSCTL,
   372  		uintptr(mibptr),
   373  		uintptr(miblen),
   374  		0,
   375  		uintptr(unsafe.Pointer(&length)),
   376  		0,
   377  		0)
   378  	if err != 0 {
   379  		return nil, length, err
   380  	}
   381  
   382  	count := int32(length / uint64(sizeOfKinfoProc))
   383  	mib = []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, count}
   384  	mibptr = unsafe.Pointer(&mib[0])
   385  	miblen = uint64(len(mib))
   386  	// get proc info itself
   387  	buf := make([]byte, length)
   388  	_, _, err = unix.Syscall6(
   389  		unix.SYS___SYSCTL,
   390  		uintptr(mibptr),
   391  		uintptr(miblen),
   392  		uintptr(unsafe.Pointer(&buf[0])),
   393  		uintptr(unsafe.Pointer(&length)),
   394  		0,
   395  		0)
   396  	if err != 0 {
   397  		return buf, length, err
   398  	}
   399  
   400  	return buf, length, nil
   401  }