<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"sync"
	"syscall"

	"github.com/golang/glog"
	_ "github.com/hyperhq/runv/cli/nsenter"
	"github.com/hyperhq/runv/hyperstart/libhyperstart"
	"github.com/hyperhq/runv/lib/term"
	"github.com/kardianos/osext"
	"github.com/kr/pty"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/urfave/cli"
)

var shimCommand = cli.Command{
	Name:     "shim",
	Usage:    "[internal command] proxy operations(io, signal ...) to the container/process",
	HideHelp: true,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name: "container",
		},
		cli.StringFlag{
			Name: "process",
		},
		cli.BoolFlag{
			Name: "proxy-exit-code",
		},
		cli.BoolFlag{
			Name: "proxy-stdio",
		},
		cli.BoolFlag{
			Name: "proxy-signal",
		},
		cli.BoolFlag{
			Name: "proxy-winsize",
		},
	},
	Before: func(context *cli.Context) error {
		return cmdPrepare(context, false, false)
	},
	Action: func(context *cli.Context) error {
		container := context.String("container")
		process := context.String("process")

		h, err := libhyperstart.NewGrpcBasedHyperstart(filepath.Join(context.GlobalString("root"), container, "sandbox", "hyperstartgrpc.sock"))
		if err != nil {
			return cli.NewExitError(fmt.Sprintf("failed to connect to hyperstart proxy: %v", err), -1)
		}

		if process == "init" {
			waitSigUsr1 := make(chan os.Signal, 1)
			signal.Notify(waitSigUsr1, syscall.SIGUSR1)
			<-waitSigUsr1
			signal.Stop(waitSigUsr1)
		}

		if context.Bool("proxy-stdio") {
			wg := &sync.WaitGroup{}
			proxyStdio(h, container, process, wg)
			defer wg.Wait()
		}

		if context.Bool("proxy-winsize") {
			glog.V(3).Infof("using shim to proxy winsize")
			s, err := term.SetRawTerminal(os.Stdin.Fd())
			if err != nil {
				return cli.NewExitError(fmt.Sprintf("failed to set raw terminal: %v", err), -1)
			}
			defer term.RestoreTerminal(os.Stdin.Fd(), s)
			monitorTtySize(h, container, process)
		}

		if context.Bool("proxy-signal") {
			glog.V(3).Infof("using shim to proxy signal")
			sigc := forwardAllSignals(h, container, process)
			defer signal.Stop(sigc)
		}

		// wait until exit
		exitcode := h.WaitProcess(container, process)
		if context.Bool("proxy-exit-code") {
			glog.V(3).Infof("using shim to proxy exit code: %d", exitcode)
			if exitcode != 0 {
				cli.NewExitError("process returns non zero exit code", exitcode)
			}
			return nil
		}

		return nil
	},
}

func proxyStdio(h libhyperstart.Hyperstart, container, process string, wg *sync.WaitGroup) {
	// don't wait the copying of the stdin, because `io.Copy(inPipe, os.Stdin)`
	// can't terminate when no input. todo: find a better way.
	wg.Add(2)
	inPipe, outPipe, errPipe := libhyperstart.StdioPipe(h, container, process)
	go func() {
		_, err1 := io.Copy(inPipe, os.Stdin)
		err2 := h.CloseStdin(container, process)
		glog.V(3).Infof("copy stdin %#v %#v", err1, err2)
	}()

	go func() {
		_, err := io.Copy(os.Stdout, outPipe)
		glog.V(3).Infof("copy stdout %#v", err)
		wg.Done()
	}()

	go func() {
		_, err := io.Copy(os.Stderr, errPipe)
		glog.V(3).Infof("copy stderr %#v", err)<commit_msg>shim: return error when proxy-exit-code if process fails<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"sync"
	"syscall"

	"github.com/golang/glog"
	_ "github.com/hyperhq/runv/cli/nsenter"
	"github.com/hyperhq/runv/hyperstart/libhyperstart"
	"github.com/hyperhq/runv/lib/term"
	"github.com/kardianos/osext"
	"github.com/kr/pty"
	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/urfave/cli"
)

var shimCommand = cli.Command{
	Name:     "shim",
	Usage:    "[internal command] proxy operations(io, signal ...) to the container/process",
	HideHelp: true,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name: "container",
		},
		cli.StringFlag{
			Name: "process",
		},
		cli.BoolFlag{
			Name: "proxy-exit-code",
		},
		cli.BoolFlag{
			Name: "proxy-stdio",
		},
		cli.BoolFlag{
			Name: "proxy-signal",
		},
		cli.BoolFlag{
			Name: "proxy-winsize",
		},
	},
	Before: func(context *cli.Context) error {
		return cmdPrepare(context, false, false)
	},
	Action: func(context *cli.Context) error {
		container := context.String("container")
		process := context.String("process")

		h, err := libhyperstart.NewGrpcBasedHyperstart(filepath.Join(context.GlobalString("root"), container, "sandbox", "hyperstartgrpc.sock"))
		if err != nil {
			return cli.NewExitError(fmt.Sprintf("failed to connect to hyperstart proxy: %v", err), -1)
		}

		if process == "init" {
			waitSigUsr1 := make(chan os.Signal, 1)
			signal.Notify(waitSigUsr1, syscall.SIGUSR1)
			<-waitSigUsr1
			signal.Stop(waitSigUsr1)
		}

		if context.Bool("proxy-stdio") {
			wg := &sync.WaitGroup{}
			proxyStdio(h, container, process, wg)
			defer wg.Wait()
		}

		if context.Bool("proxy-winsize") {
			glog.V(3).Infof("using shim to proxy winsize")
			s, err := term.SetRawTerminal(os.Stdin.Fd())
			if err != nil {
				return cli.NewExitError(fmt.Sprintf("failed to set raw terminal: %v", err), -1)
			}
			defer term.RestoreTerminal(os.Stdin.Fd(), s)
			monitorTtySize(h, container, process)
		}

		if context.Bool("proxy-signal") {
			glog.V(3).Infof("using shim to proxy signal")
			sigc := forwardAllSignals(h, container, process)
			defer signal.Stop(sigc)
		}

		// wait until exit
		exitcode := h.WaitProcess(container, process)
		if context.Bool("proxy-exit-code") {
			glog.V(3).Infof("using shim to proxy exit code: %d", exitcode)
			if exitcode != 0 {
				return cli.NewExitError("process returns non zero exit code", exitcode)
			}
			return nil
		}

		return nil
	},
}

func proxyStdio(h libhyperstart.Hyperstart, container, process string, wg *sync.WaitGroup) {
	// don't wait the copying of the stdin, because `io.Copy(inPipe, os.Stdin)`
	// can't terminate when no input. todo: find a better way.
	wg.Add(2)
	inPipe, outPipe, errPipe := libhyperstart.StdioPipe(h, container, process)
	go func() {
		_, err1 := io.Copy(inPipe, os.Stdin)
		err2 := h.CloseStdin(container, process)
		glog.V(3).Infof("copy stdin %#v %#v", err1, err2)
	}()

	go func() {
		_, err := io.Copy(os.Stdout, outPipe)
		glog.V(3).Infof("copy stdout %#v", err)
		wg.Done()
	}()

	go func() {
		_, err := io.Copy(os.Stderr, errPipe)
		glog.V(3).Infof("copy stderr %#v", err)
{'fraction_non_alphanumeric': 0.16281645569620254, 'fraction_numerical': 0.006329113924050633, 'mean_word_length': 3.7562076749435667, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3541025', 'n_tokens_mistral': 2700, 'n_tokens_neox': 2440, 'n_words': 509}