github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/control/cgroups.go (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //	http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package control
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel"
    23  )
    24  
    25  // Cgroups contains the state for cgroupfs related control commands.
    26  type Cgroups struct {
    27  	Kernel *kernel.Kernel
    28  }
    29  
    30  func (c *Cgroups) findCgroup(ctx context.Context, file CgroupControlFile) (kernel.Cgroup, error) {
    31  	ctl, err := file.controller()
    32  	if err != nil {
    33  		return kernel.Cgroup{}, err
    34  	}
    35  	return c.Kernel.CgroupRegistry().FindCgroup(ctx, ctl, file.Path)
    36  }
    37  
    38  // CgroupControlFile identifies a specific control file within a
    39  // specific cgroup, for the hierarchy with a given controller.
    40  type CgroupControlFile struct {
    41  	Controller string `json:"controller"`
    42  	Path       string `json:"path"`
    43  	Name       string `json:"name"`
    44  }
    45  
    46  func (f *CgroupControlFile) controller() (kernel.CgroupControllerType, error) {
    47  	return kernel.ParseCgroupController(f.Controller)
    48  }
    49  
    50  // CgroupsResult represents the result of a cgroup operation.
    51  type CgroupsResult struct {
    52  	Data    string `json:"value"`
    53  	IsError bool   `json:"is_error"`
    54  }
    55  
    56  // AsError interprets the result as an error.
    57  func (r *CgroupsResult) AsError() error {
    58  	if r.IsError {
    59  		return fmt.Errorf(r.Data)
    60  	}
    61  	return nil
    62  }
    63  
    64  // Unpack splits CgroupsResult into a (value, error) tuple.
    65  func (r *CgroupsResult) Unpack() (string, error) {
    66  	if r.IsError {
    67  		return "", fmt.Errorf(r.Data)
    68  	}
    69  	return r.Data, nil
    70  }
    71  
    72  func newValue(val string) CgroupsResult {
    73  	return CgroupsResult{
    74  		Data: strings.TrimSpace(val),
    75  	}
    76  }
    77  
    78  func newError(err error) CgroupsResult {
    79  	return CgroupsResult{
    80  		Data:    err.Error(),
    81  		IsError: true,
    82  	}
    83  }
    84  
    85  // CgroupsResults represents the list of results for a batch command.
    86  type CgroupsResults struct {
    87  	Results []CgroupsResult `json:"results"`
    88  }
    89  
    90  func (o *CgroupsResults) appendValue(val string) {
    91  	o.Results = append(o.Results, newValue(val))
    92  }
    93  
    94  func (o *CgroupsResults) appendError(err error) {
    95  	o.Results = append(o.Results, newError(err))
    96  }
    97  
    98  // CgroupsReadArg represents the arguments for a single read command.
    99  type CgroupsReadArg struct {
   100  	File CgroupControlFile `json:"file"`
   101  }
   102  
   103  // CgroupsReadArgs represents the list of arguments for a batched read command.
   104  type CgroupsReadArgs struct {
   105  	Args []CgroupsReadArg `json:"args"`
   106  }
   107  
   108  // ReadControlFiles is an RPC stub for batch-reading cgroupfs control files.
   109  func (c *Cgroups) ReadControlFiles(args *CgroupsReadArgs, out *CgroupsResults) error {
   110  	ctx := c.Kernel.SupervisorContext()
   111  	for _, arg := range args.Args {
   112  		cg, err := c.findCgroup(ctx, arg.File)
   113  		if err != nil {
   114  			out.appendError(err)
   115  			continue
   116  		}
   117  
   118  		val, err := cg.ReadControl(ctx, arg.File.Name)
   119  		if err != nil {
   120  			out.appendError(err)
   121  		} else {
   122  			out.appendValue(val)
   123  		}
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  // CgroupsWriteArg represents the arguments for a single write command.
   130  type CgroupsWriteArg struct {
   131  	File  CgroupControlFile `json:"file"`
   132  	Value string            `json:"value"`
   133  }
   134  
   135  // CgroupsWriteArgs represents the lust of arguments for a batched write command.
   136  type CgroupsWriteArgs struct {
   137  	Args []CgroupsWriteArg `json:"args"`
   138  }
   139  
   140  // WriteControlFiles is an RPC stub for batch-writing cgroupfs control files.
   141  func (c *Cgroups) WriteControlFiles(args *CgroupsWriteArgs, out *CgroupsResults) error {
   142  	ctx := c.Kernel.SupervisorContext()
   143  
   144  	for _, arg := range args.Args {
   145  		cg, err := c.findCgroup(ctx, arg.File)
   146  		if err != nil {
   147  			out.appendError(err)
   148  			continue
   149  		}
   150  
   151  		err = cg.WriteControl(ctx, arg.File.Name, arg.Value)
   152  		if err != nil {
   153  			out.appendError(err)
   154  		} else {
   155  			out.appendValue("")
   156  		}
   157  	}
   158  	return nil
   159  }