代码拉取完成,页面将自动刷新
package nsenter
import (
"bufio"
"bytes"
"context"
"errors"
"fmt"
"gitee.com/anesec/chameleon/pkg/utils"
"io"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"sync/atomic"
)
type NsType int
type Runnable interface {
Run(context.Context) error
}
func HTTP(options ...Option) (*http.Client, error) {
handle, err := newNsHandle(options...)
if err != nil {
return nil, err
}
return handle.createHTTPClient(), nil
}
// Execute executes runnable object in specified namespaces
func Execute(ctx context.Context, runnable Runnable, options ...Option) error {
handle, err := newNsHandle(options...)
if err != nil {
return err
}
return handle.Execute(ctx, runnable)
}
// Stat reads file stat information in specified mount namespace bind to pid and procfs
func Stat(name string, options ...Option) (os.FileInfo, error) {
var opts = nsOptions{procfs: "/proc"}
for _, o := range options {
o.apply(&opts)
}
if opts.pid <= 0 {
opts.pid = 1
}
info, err := os.Stat(filepath.Join(opts.procfs, strconv.Itoa(opts.pid), "/root", name))
if err != nil {
var pe *os.PathError
if errors.As(err, &pe) {
pe.Path = name
}
}
return info, err
}
// ReadFile reads file in specified mount namespace bind to pid and procfs
func ReadFile(name string, options ...Option) ([]byte, error) {
var opts = nsOptions{procfs: "/proc"}
for _, o := range options {
o.apply(&opts)
}
if opts.pid <= 0 {
opts.pid = 1
}
data, err := os.ReadFile(filepath.Join(opts.procfs, strconv.Itoa(opts.pid), "/root", name))
if err != nil {
var pe *os.PathError
if errors.As(err, &pe) {
pe.Path = name
}
}
return data, err
}
// WriteFile writes file in specified mount namespace bind to pid and procfs
func WriteFile(name string, data []byte, perm os.FileMode, options ...Option) error {
var opts = nsOptions{procfs: "/proc"}
for _, o := range options {
o.apply(&opts)
}
if opts.pid <= 0 {
opts.pid = 1
}
err := os.WriteFile(filepath.Join(opts.procfs, strconv.Itoa(opts.pid), "/root", name), data, perm)
if err != nil {
var pe *os.PathError
if errors.As(err, &pe) {
pe.Path = name
}
}
return err
}
// MountNsId reads mount namespace inode for specified process id and host procfs
func MountNsId(options ...Option) (uint32, error) {
return readNsId("mnt", options...)
}
// PidNsId reads pid namespace inode for specified process id and host procfs
func PidNsId(options ...Option) (uint32, error) {
return readNsId("pid", options...)
}
// NetNsId reads net namespace inode for specified process id and host procfs
func NetNsId(options ...Option) (uint32, error) {
return readNsId("net", options...)
}
// CGroupNsId reads cgroup namespace inode for specified process id and host procfs
func CGroupNsId(options ...Option) (uint32, error) {
return readNsId("cgroup", options...)
}
// UserNsId reads user namespace inode for specified process id and host procfs
func UserNsId(options ...Option) (uint32, error) {
return readNsId("user", options...)
}
// UtsNsId reads uts namespace inode for specified process id and host procfs
func UtsNsId(options ...Option) (uint32, error) {
return readNsId("uts", options...)
}
func readNsId(ns string, options ...Option) (uint32, error) {
var opts = nsOptions{procfs: "/proc"}
for _, o := range options {
o.apply(&opts)
}
if opts.pid <= 0 {
return 0, errors.New("namespace attached pid is not specified")
}
realPath, err := os.Readlink(filepath.Join(opts.procfs, fmt.Sprintf("/%d/ns", opts.pid), ns))
if err != nil {
return 0, err
}
index := strings.Index(realPath, "[")
if index == -1 {
return 0, fmt.Errorf("unable to parse namespace id from %q", realPath)
}
id := realPath[index+1 : len(realPath)-1]
u64, err := strconv.ParseUint(id, 10, 32)
if err != nil {
return 0, fmt.Errorf("unable to convert namespace id (%s) to uint64 value", realPath)
}
return uint32(u64), nil
}
// NewScanner creates a namespace file scanner, which reads lines of specified file from mount namespace bind
// with process id(host pid namespace) and host procfs
func NewScanner(filename string, options ...Option) (*Scanner, error) {
var opts = nsOptions{procfs: "/proc"}
for _, o := range options {
o.apply(&opts)
}
if opts.pid <= 0 {
opts.pid = 1
}
scanner := &Scanner{}
var (
file *os.File
info os.FileInfo
size utils.BufferSize
err error
)
if file, err = os.Open(filepath.Join(opts.procfs, strconv.Itoa(opts.pid), "/root", filename)); err != nil {
goto pathErr
}
if info, err = Stat(filename); err != nil {
goto pathErr
}
size = utils.BufferSize(info.Size())
if size <= utils.BufferSize16K {
buf := utils.Acquire(size)
if _, err = io.Copy(buf, file); err != nil {
goto pathErr
}
scanner.buf.Store(buf)
} else {
bs := bufio.NewScanner(file)
scanner.bs.Store(bs)
}
scanner.rc.Store(file)
return scanner, nil
pathErr:
var pe *os.PathError
if errors.As(err, &pe) {
pe.Path = filename
}
return nil, err
}
// Scanner represents a namespace file scanner which return a line for each Next() call
type Scanner struct {
rc atomic.Pointer[os.File]
buf atomic.Pointer[bytes.Buffer]
bs atomic.Pointer[bufio.Scanner]
}
// Next returns line of opened file, if returned data is nil, indicates EOF or failed with specified error
func (s *Scanner) Next() ([]byte, error) {
if buf := s.buf.Load(); buf != nil {
advance, data, _ := bufio.ScanLines(buf.Bytes(), true)
if advance > 0 {
buf.Next(advance)
} else {
return nil, nil
}
return data, nil
} else if bs := s.bs.Load(); bs != nil {
if !bs.Scan() {
return nil, bs.Err()
}
return bs.Bytes(), nil
}
return nil, nil
}
func (s *Scanner) Close() error {
var err error
if rc := s.rc.Swap(nil); rc != nil {
err = rc.Close()
}
if buf := s.buf.Swap(nil); buf != nil {
utils.Recycle(buf)
}
s.bs.Store(nil)
return err
}
// NewReader creates a namespace file reader, which reads file from mount namespace bind
// with process id(host pid namespace) and host procfs
func NewReader(filename string, options ...Option) (io.ReadCloser, error) {
var opts = nsOptions{procfs: "/proc"}
for _, o := range options {
o.apply(&opts)
}
if opts.pid <= 0 {
opts.pid = 1
}
nr := &nsReader{}
var (
file *os.File
err error
)
if file, err = os.Open(filepath.Join(opts.procfs, strconv.Itoa(opts.pid), "/root", filename)); err != nil {
var pe *os.PathError
if errors.As(err, &pe) {
pe.Path = filename
}
return nil, err
}
nr.rc.Store(file)
return nr, nil
}
type nsReader struct {
rc atomic.Pointer[os.File]
}
func (nr *nsReader) Read(p []byte) (n int, err error) {
if rc := nr.rc.Load(); rc != nil {
return rc.Read(p)
}
return 0, os.ErrInvalid
}
func (nr *nsReader) Close() error {
if rc := nr.rc.Swap(nil); rc != nil {
return rc.Close()
}
return nil
}
// NewWriter creates a namespace file writer, which write file to mount namespace bind
// with process id(host pid namespace) and host procfs
func NewWriter(filename string, flag int, perm os.FileMode, options ...Option) (io.WriteCloser, error) {
var opts = nsOptions{procfs: "/proc"}
for _, o := range options {
o.apply(&opts)
}
if opts.pid <= 0 {
opts.pid = 1
}
nw := &nsWriter{}
var (
file *os.File
err error
)
if file, err = os.OpenFile(filepath.Join(opts.procfs, strconv.Itoa(opts.pid), "/root", filename), flag, perm); err != nil {
var pe *os.PathError
if errors.As(err, &pe) {
pe.Path = filename
}
return nil, err
}
nw.wc.Store(file)
return nw, err
}
type nsWriter struct {
wc atomic.Pointer[os.File]
}
func (nw *nsWriter) Write(p []byte) (n int, err error) {
if wc := nw.wc.Load(); wc != nil {
return wc.Write(p)
}
return 0, os.ErrInvalid
}
func (nw *nsWriter) Close() error {
if wc := nw.wc.Swap(nil); wc != nil {
return wc.Close()
}
return nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。