github.com/gofiber/fiber/v2@v2.47.0/internal/gopsutil/process/process_posix.go (about)

     1  //go:build linux || freebsd || openbsd || darwin
     2  // +build linux freebsd openbsd darwin
     3  
     4  package process
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"os"
    10  	"os/user"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  	"syscall"
    15  
    16  	"github.com/gofiber/fiber/v2/internal/gopsutil/common"
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  // POSIX
    21  func getTerminalMap() (map[uint64]string, error) {
    22  	ret := make(map[uint64]string)
    23  	var termfiles []string
    24  
    25  	d, err := os.Open("/dev")
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  	defer d.Close()
    30  
    31  	devnames, err := d.Readdirnames(-1)
    32  	if err != nil {
    33  		return nil, err
    34  	}
    35  	for _, devname := range devnames {
    36  		if strings.HasPrefix(devname, "/dev/tty") {
    37  			termfiles = append(termfiles, "/dev/tty/"+devname)
    38  		}
    39  	}
    40  
    41  	var ptsnames []string
    42  	ptsd, err := os.Open("/dev/pts")
    43  	if err != nil {
    44  		ptsnames, _ = filepath.Glob("/dev/ttyp*")
    45  		if ptsnames == nil {
    46  			return nil, err
    47  		}
    48  	}
    49  	defer ptsd.Close()
    50  
    51  	if ptsnames == nil {
    52  		defer ptsd.Close()
    53  		ptsnames, err = ptsd.Readdirnames(-1)
    54  		if err != nil {
    55  			return nil, err
    56  		}
    57  		for _, ptsname := range ptsnames {
    58  			termfiles = append(termfiles, "/dev/pts/"+ptsname)
    59  		}
    60  	} else {
    61  		termfiles = ptsnames
    62  	}
    63  
    64  	for _, name := range termfiles {
    65  		stat := unix.Stat_t{}
    66  		if err = unix.Stat(name, &stat); err != nil {
    67  			return nil, err
    68  		}
    69  		rdev := uint64(stat.Rdev)
    70  		ret[rdev] = strings.Replace(name, "/dev", "", -1)
    71  	}
    72  	return ret, nil
    73  }
    74  
    75  func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) {
    76  	if pid <= 0 {
    77  		return false, fmt.Errorf("invalid pid %v", pid)
    78  	}
    79  	proc, err := os.FindProcess(int(pid))
    80  	if err != nil {
    81  		return false, err
    82  	}
    83  
    84  	if _, err := os.Stat(common.HostProc()); err == nil { //Means that proc filesystem exist
    85  		// Checking PID existence based on existence of /<HOST_PROC>/proc/<PID> folder
    86  		// This covers the case when running inside container with a different process namespace (by default)
    87  
    88  		_, err := os.Stat(common.HostProc(strconv.Itoa(int(pid))))
    89  		if os.IsNotExist(err) {
    90  			return false, nil
    91  		}
    92  		return err == nil, err
    93  	}
    94  
    95  	//'/proc' filesystem is not exist, checking of PID existence is done via signalling the process
    96  	//Make sense only if we run in the same process namespace
    97  	err = proc.Signal(syscall.Signal(0))
    98  	if err == nil {
    99  		return true, nil
   100  	}
   101  	if err.Error() == "os: process already finished" {
   102  		return false, nil
   103  	}
   104  	errno, ok := err.(syscall.Errno)
   105  	if !ok {
   106  		return false, err
   107  	}
   108  	switch errno {
   109  	case syscall.ESRCH:
   110  		return false, nil
   111  	case syscall.EPERM:
   112  		return true, nil
   113  	}
   114  
   115  	return false, err
   116  }
   117  
   118  // SendSignal sends a unix.Signal to the process.
   119  // Currently, SIGSTOP, SIGCONT, SIGTERM and SIGKILL are supported.
   120  func (p *Process) SendSignal(sig syscall.Signal) error {
   121  	return p.SendSignalWithContext(context.Background(), sig)
   122  }
   123  
   124  func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error {
   125  	process, err := os.FindProcess(int(p.Pid))
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	err = process.Signal(sig)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  // Suspend sends SIGSTOP to the process.
   139  func (p *Process) Suspend() error {
   140  	return p.SuspendWithContext(context.Background())
   141  }
   142  
   143  func (p *Process) SuspendWithContext(ctx context.Context) error {
   144  	return p.SendSignal(unix.SIGSTOP)
   145  }
   146  
   147  // Resume sends SIGCONT to the process.
   148  func (p *Process) Resume() error {
   149  	return p.ResumeWithContext(context.Background())
   150  }
   151  
   152  func (p *Process) ResumeWithContext(ctx context.Context) error {
   153  	return p.SendSignal(unix.SIGCONT)
   154  }
   155  
   156  // Terminate sends SIGTERM to the process.
   157  func (p *Process) Terminate() error {
   158  	return p.TerminateWithContext(context.Background())
   159  }
   160  
   161  func (p *Process) TerminateWithContext(ctx context.Context) error {
   162  	return p.SendSignal(unix.SIGTERM)
   163  }
   164  
   165  // Kill sends SIGKILL to the process.
   166  func (p *Process) Kill() error {
   167  	return p.KillWithContext(context.Background())
   168  }
   169  
   170  func (p *Process) KillWithContext(ctx context.Context) error {
   171  	return p.SendSignal(unix.SIGKILL)
   172  }
   173  
   174  // Username returns a username of the process.
   175  func (p *Process) Username() (string, error) {
   176  	return p.UsernameWithContext(context.Background())
   177  }
   178  
   179  func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
   180  	uids, err := p.Uids()
   181  	if err != nil {
   182  		return "", err
   183  	}
   184  	if len(uids) > 0 {
   185  		u, err := user.LookupId(strconv.Itoa(int(uids[0])))
   186  		if err != nil {
   187  			return "", err
   188  		}
   189  		return u.Username, nil
   190  	}
   191  	return "", nil
   192  }