...

Text file src/golang.conradwood.net/go-easyops/linux/cgroup_com.go~

Documentation: golang.conradwood.net/go-easyops/linux

     1package linux
     2
     3import (
     4	"context"
     5	"fmt"
     6	"io"
     7	"os"
     8	"os/exec"
     9	"os/user"
    10	"strconv"
    11	"sync"
    12	"syscall"
    13
    14	"golang.conradwood.net/go-easyops/errors"
    15	"golang.conradwood.net/go-easyops/utils"
    16	"golang.org/x/sys/unix"
    17)
    18
    19/*
    20   CGROUP Permissions:
    21
    22   clone3 returns -EACCESS (permission denied) unless:
    23   the user has write access to cgroup.procs in the nearest common ancestor director of calling process and cgroup of new process.
    24
    25   EXAMPLES: (assuming only LINUXCOM and below is user-writeable)
    26   | CALLING_PROC                    | NEW_PROC                           | Result   |
    27   +---------------------------------+------------------------------------+----------+
    28   | /sys/fs/cgroup/LINUXCOM/me/     | /sys/fs/cgroup/LINUXCOM/com_1/     | EACCESS  |
    29   | /sys/fs/cgroup/LINUXCOM/foo/me/ | /sys/fs/cgroup/LINUXCOM/com_1/     | EACCESS  |
    30   | /sys/fs/cgroup/LINUXCOM/foo/me/ | /sys/fs/cgroup/LINUXCOM/foo/com_1/ | OK       |
    31*/
    32
    33var (
    34	command_ctr = 0
    35	ctrlock     sync.Mutex
    36)
    37
    38type Command interface {
    39	SigInt() error  // -2
    40	SigKill() error // -9
    41}
    42type command struct {
    43	cgroupdir    string
    44	stdinwriter  io.Writer
    45	stdoutreader io.Reader
    46	stderrreader io.Reader
    47	instance     *cominstance
    48}
    49type cominstance struct {
    50	exe             []string
    51	command         *command
    52	cgroupdir_cmd   string
    53	com             *exec.Cmd
    54	stdout_pipe     io.ReadCloser
    55	stderr_pipe     io.ReadCloser
    56	defStdoutReader *comDefaultReader
    57	defStderrReader *comDefaultReader
    58}
    59
    60func NewCommand() *command {
    61	return &command{
    62		cgroupdir: "/sys/fs/cgroup/LINUXCOM/ancestor/",
    63	}
    64}
    65
    66// e.g. /sys/fs/cgroup/LINUXCOM
    67func (c *command) SetCGroupDir(dir string) {
    68	c.cgroupdir = dir
    69}
    70
    71func (c *command) StdinWriter(r io.Writer) {
    72	c.stdinwriter = r
    73}
    74func (c *command) StdoutReader(r io.Reader) {
    75	c.stdoutreader = r
    76}
    77func (c *command) StderrReader(r io.Reader) {
    78	c.stderrreader = r
    79}
    80func (c *command) IsRunning() bool {
    81	return true
    82}
    83
    84// failed to start, then error
    85func (c *command) Start(ctx context.Context, com ...string) (*cominstance, error) {
    86	n := newctr()
    87	cgroupdir_cmd := fmt.Sprintf("%s/com_%d", c.cgroupdir, n)
    88	err := mkdir(cgroupdir_cmd + "/tasks")
    89	if err != nil {
    90		return nil, err
    91	}
    92	ci := &cominstance{command: c, cgroupdir_cmd: cgroupdir_cmd}
    93	c.instance = ci
    94	return ci, ci.start(ctx, com...)
    95}
    96func (ci *cominstance) start(ctx context.Context, com ...string) error {
    97	u, err := user.Current()
    98	if err != nil {
    99		return errors.Wrap(err)
   100	}
   101
   102	uid, err := strconv.Atoi(u.Uid)
   103	if err != nil {
   104		return errors.Wrap(err)
   105	}
   106
   107	gid, err := strconv.Atoi(u.Gid)
   108	if err != nil {
   109		return errors.Wrap(err)
   110	}
   111
   112	// open cgroup filedescriptor
   113	cgroup_fd_path := ci.cgroupdir_cmd
   114	cgroup_fd, err := syscall.Open(cgroup_fd_path, unix.O_PATH, 0)
   115	if err != nil {
   116		return errors.Wrap(err)
   117	}
   118	fmt.Printf("CgroupFD for \"%s\": %d\n", cgroup_fd_path, cgroup_fd)
   119	ci.com = exec.CommandContext(ctx, com[0], com[1:]...)
   120	ci.stdout_pipe, err = ci.com.StdoutPipe()
   121	if err != nil {
   122		return err
   123	}
   124	ci.defStdoutReader = newDefaultReader(ci.stdout_pipe)
   125	ci.stderr_pipe, err = ci.com.StderrPipe()
   126	if err != nil {
   127		return err
   128	}
   129	ci.defStderrReader = newDefaultReader(ci.stdout_pipe)
   130
   131	ci.com.SysProcAttr = &syscall.SysProcAttr{
   132		Credential: &syscall.Credential{
   133			Uid:         uint32(uid),
   134			Gid:         uint32(gid),
   135			NoSetGroups: true,
   136		},
   137		UseCgroupFD: true,
   138		CgroupFD:    cgroup_fd,
   139	}
   140	err = ci.com.Start()
   141	if err != nil {
   142		return errors.Wrap(err)
   143	}
   144	return nil
   145}
   146
   147func (ci *cominstance) Wait(ctx context.Context) error {
   148	if ci.com == nil {
   149		return nil
   150	}
   151	err := ci.com.Wait()
   152	return err
   153}
   154func (c *command) ExitCode() int {
   155	return 0
   156}
   157func (c *command) CombinedOutput() []byte {
   158	return nil
   159}
   160func (c *command) SigInt() error { // -2
   161	fmt.Printf("sending sigint\n")
   162	return c.sendsig(syscall.SIGINT)
   163}
   164func (c *command) SigKill() error { // -9
   165	fmt.Printf("sending sigkill\n")
   166	return c.sendsig(syscall.SIGKILL)
   167}
   168
   169func (c *command) sendsig(sig syscall.Signal) error {
   170	ci := c.instance
   171	pids, err := get_pids_for_cgroup(ci.cgroupdir_cmd)
   172	if err != nil {
   173		fmt.Printf("Could not get pids for cgroup \"%s\": %s\n", ci.cgroupdir_cmd, err)
   174		return err
   175	}
   176	fmt.Printf("Cgroupdir \"%s\" has %d pids\n", ci.cgroupdir_cmd, len(pids))
   177	for _, pid := range pids {
   178		fmt.Printf("Sending signal %v to pid %d\n", sig, pid)
   179		err = syscall.Kill(int(pid), sig)
   180		if err != nil {
   181			return errors.Wrap(err)
   182		}
   183	}
   184	return nil
   185}
   186
   187func newctr() int {
   188	ctrlock.Lock()
   189	command_ctr++
   190	res := command_ctr
   191	ctrlock.Unlock()
   192	return res
   193}
   194
   195func mkdir(dir string) error {
   196	if utils.FileExists(dir) {
   197		return nil
   198	}
   199	err := os.MkdirAll(dir, 0777)
   200	if err != nil {
   201		return errors.Wrap(err)
   202	}
   203	if !utils.FileExists(dir) {
   204		return errors.Errorf("failed to create \"%s\"", dir)
   205	}
   206	return nil
   207}

View as plain text