Path: blob/main/components/ws-daemon/pkg/nsinsider/nsinsider.go
2499 views
// Copyright (c) 2022 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package nsinsider56import (7"bytes"8"fmt"9"io"10"os"11"os/exec"12"path/filepath"1314"github.com/gitpod-io/gitpod/common-go/log"15"golang.org/x/sys/unix"16)1718type NsinsiderOpts struct {19MountNS bool20PidNS bool21NetNS bool22MountNSPid int23}2425func EnterMountNS(enter bool) nsinsiderOpt {26return func(o *NsinsiderOpts) {27o.MountNS = enter28}29}3031func EnterPidNS(enter bool) nsinsiderOpt {32return func(o *NsinsiderOpts) {33o.PidNS = enter34}35}3637func EnterNetNS(enter bool) nsinsiderOpt {38return func(o *NsinsiderOpts) {39o.NetNS = enter40}41}4243func EnterMountNSPid(pid int) nsinsiderOpt {44return func(o *NsinsiderOpts) {45o.MountNS = true46o.MountNSPid = pid47}48}4950type nsinsiderOpt func(*NsinsiderOpts)5152func Nsinsider(instanceID string, targetPid int, mod func(*exec.Cmd), opts ...nsinsiderOpt) error {53cfg := NsinsiderOpts{54MountNS: true,55}56for _, o := range opts {57o(&cfg)58}5960base, err := os.Executable()61if err != nil {62return err63}6465type mnt struct {66Env string67Source string68Flags int69}70var nss []mnt71if cfg.MountNS {72tpid := targetPid73if cfg.MountNSPid != 0 {74tpid = cfg.MountNSPid75}76nss = append(nss,77mnt{"_LIBNSENTER_ROOTFD", fmt.Sprintf("/proc/%d/root", tpid), unix.O_PATH},78mnt{"_LIBNSENTER_CWDFD", fmt.Sprintf("/proc/%d/cwd", tpid), unix.O_PATH},79mnt{"_LIBNSENTER_MNTNSFD", fmt.Sprintf("/proc/%d/ns/mnt", tpid), os.O_RDONLY},80)81}82if cfg.PidNS {83nss = append(nss, mnt{"_LIBNSENTER_PIDNSFD", fmt.Sprintf("/proc/%d/ns/pid", targetPid), os.O_RDONLY})84}85if cfg.NetNS {86nss = append(nss, mnt{"_LIBNSENTER_NETNSFD", fmt.Sprintf("/proc/%d/ns/net", targetPid), os.O_RDONLY})87}8889stdioFdCount := 390cmd := exec.Command(filepath.Join(filepath.Dir(base), "nsinsider"))91mod(cmd)92cmd.Env = append(cmd.Env, "_LIBNSENTER_INIT=1", "GITPOD_INSTANCE_ID="+instanceID)93for _, ns := range nss {94f, err := os.OpenFile(ns.Source, ns.Flags, 0)95if err != nil {96return fmt.Errorf("cannot open %s: %w", ns.Source, err)97}98defer f.Close()99cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%d", ns.Env, stdioFdCount+len(cmd.ExtraFiles)))100cmd.ExtraFiles = append(cmd.ExtraFiles, f)101}102103var cmdOut bytes.Buffer104var cmdErr bytes.Buffer105cmd.Stdout = &cmdOut106cmd.Stderr = &cmdErr // gpl: Why to we write the stderr to os.StdErr? Not sure, so keeping the behavior here...107cmd.Stdin = os.Stdin108err = cmd.Run()109_, _ = io.Copy(os.Stderr, &cmdErr)110log.FromBuffer(&cmdOut, log.WithFields(log.OWI("", "", instanceID)))111if err != nil {112// writing stderr to the error so clients can pattern match on specific errors113return fmt.Errorf("run nsinsider (%v) failed: %q \\ %q\n%v",114cmd.Args,115cmdOut.String(),116cmdErr.String(),117err,118)119}120return nil121}122123124