<commit_before>package runcexecutor

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/containerd/console"
	"github.com/containerd/containerd"
	runc "github.com/containerd/go-runc"
	"github.com/docker/docker/pkg/signal"
	"github.com/moby/buildkit/executor"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/sync/errgroup"
)

func (w *runcExecutor) run(ctx context.Context, id, bundle string, process executor.ProcessInfo) (int, error) {
	return w.callWithIO(ctx, id, bundle, process, func(ctx context.Context, pidfile string, io runc.IO) (int, error) {
		return w.runc.Run(ctx, id, bundle, &runc.CreateOpts{
			NoPivot: w.noPivot,
			PidFile: pidfile,
			IO:      io,
		})
	})
}

func (w *runcExecutor) exec(ctx context.Context, id, bundle string, specsProcess *specs.Process, process executor.ProcessInfo) (int, error) {
	return w.callWithIO(ctx, id, bundle, process, func(ctx context.Context, pidfile string, io runc.IO) (int, error) {
		err := w.runc.Exec(ctx, id, *specsProcess, &runc.ExecOpts{
			PidFile: pidfile,
			IO:      io,
		})
		var exitError *exec.ExitError
		if errors.As(err, &exitError) {
			return exitError.ExitCode(), err
		}
		if err != nil {
			return containerd.UnknownExitStatus, err
		}
		return 0, nil
	})
}

type runcCall func(ctx context.Context, pidfile string, io runc.IO) (int, error)

func (w *runcExecutor) callWithIO(ctx context.Context, id, bundle string, process executor.ProcessInfo, call runcCall) (int, error) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	pidfile, err := ioutil.TempFile(bundle, "*.pid")
	if err != nil {
		return containerd.UnknownExitStatus, errors.Wrap(err, "failed to create pidfile")
	}
	defer os.Remove(pidfile.Name())
	pidfile.Close()

	if !process.Meta.Tty {
		return call(ctx, pidfile.Name(), &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr})
	}

	ptm, ptsName, err := console.NewPty()
	if err != nil {
		return containerd.UnknownExitStatus, err
	}

	pts, err := os.OpenFile(ptsName, os.O_RDWR|syscall.O_NOCTTY, 0)
	if err != nil {
		ptm.Close()
		return containerd.UnknownExitStatus, err
	}

	eg, ctx := errgroup.WithContext(ctx)

	defer func() {
		if process.Stdin != nil {
			process.Stdin.Close()
		}
		pts.Close()
		ptm.Close()
		cancel() // this will shutdown resize loop
		err := eg.Wait()
		if err != nil {
			logrus.Warningf("error while shutting down tty io: %s", err)
		}
	}()

	if process.Stdin != nil {
		eg.Go(func() error {
			_, err := io.Copy(ptm, process.Stdin)
			// stdin might be a pipe, so this is like EOF
			if errors.Is(err, io.ErrClosedPipe) {
				return nil
			}
			return err
		})
	}

	if process.Stdout != nil {
		eg.Go(func() error {
			_, err := io.Copy(process.Stdout, ptm)
			// ignore `read /dev/ptmx: input/output error` when ptm is closed
			var ptmClosedError *os.PathError
			if errors.As(err, &ptmClosedError) {
				if ptmClosedError.Op == "read" &&
					ptmClosedError.Path == "/dev/ptmx" &&
					ptmClosedError.Err == syscall.Errno(0x5) {
					return nil
				}
			}
			return err
		})
	}

	eg.Go(func() error {<commit_msg>syscall.Errno(0x5) == syscall.EIO<commit_after>package runcexecutor

import (
	"bufio"
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/containerd/console"
	"github.com/containerd/containerd"
	runc "github.com/containerd/go-runc"
	"github.com/docker/docker/pkg/signal"
	"github.com/moby/buildkit/executor"
	"github.com/opencontainers/runtime-spec/specs-go"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/sync/errgroup"
)

func (w *runcExecutor) run(ctx context.Context, id, bundle string, process executor.ProcessInfo) (int, error) {
	return w.callWithIO(ctx, id, bundle, process, func(ctx context.Context, pidfile string, io runc.IO) (int, error) {
		return w.runc.Run(ctx, id, bundle, &runc.CreateOpts{
			NoPivot: w.noPivot,
			PidFile: pidfile,
			IO:      io,
		})
	})
}

func (w *runcExecutor) exec(ctx context.Context, id, bundle string, specsProcess *specs.Process, process executor.ProcessInfo) (int, error) {
	return w.callWithIO(ctx, id, bundle, process, func(ctx context.Context, pidfile string, io runc.IO) (int, error) {
		err := w.runc.Exec(ctx, id, *specsProcess, &runc.ExecOpts{
			PidFile: pidfile,
			IO:      io,
		})
		var exitError *exec.ExitError
		if errors.As(err, &exitError) {
			return exitError.ExitCode(), err
		}
		if err != nil {
			return containerd.UnknownExitStatus, err
		}
		return 0, nil
	})
}

type runcCall func(ctx context.Context, pidfile string, io runc.IO) (int, error)

func (w *runcExecutor) callWithIO(ctx context.Context, id, bundle string, process executor.ProcessInfo, call runcCall) (int, error) {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	pidfile, err := ioutil.TempFile(bundle, "*.pid")
	if err != nil {
		return containerd.UnknownExitStatus, errors.Wrap(err, "failed to create pidfile")
	}
	defer os.Remove(pidfile.Name())
	pidfile.Close()

	if !process.Meta.Tty {
		return call(ctx, pidfile.Name(), &forwardIO{stdin: process.Stdin, stdout: process.Stdout, stderr: process.Stderr})
	}

	ptm, ptsName, err := console.NewPty()
	if err != nil {
		return containerd.UnknownExitStatus, err
	}

	pts, err := os.OpenFile(ptsName, os.O_RDWR|syscall.O_NOCTTY, 0)
	if err != nil {
		ptm.Close()
		return containerd.UnknownExitStatus, err
	}

	eg, ctx := errgroup.WithContext(ctx)

	defer func() {
		if process.Stdin != nil {
			process.Stdin.Close()
		}
		pts.Close()
		ptm.Close()
		cancel() // this will shutdown resize loop
		err := eg.Wait()
		if err != nil {
			logrus.Warningf("error while shutting down tty io: %s", err)
		}
	}()

	if process.Stdin != nil {
		eg.Go(func() error {
			_, err := io.Copy(ptm, process.Stdin)
			// stdin might be a pipe, so this is like EOF
			if errors.Is(err, io.ErrClosedPipe) {
				return nil
			}
			return err
		})
	}

	if process.Stdout != nil {
		eg.Go(func() error {
			_, err := io.Copy(process.Stdout, ptm)
			// ignore `read /dev/ptmx: input/output error` when ptm is closed
			var ptmClosedError *os.PathError
			if errors.As(err, &ptmClosedError) {
				if ptmClosedError.Op == "read" &&
					ptmClosedError.Path == "/dev/ptmx" &&
					ptmClosedError.Err == syscall.EIO {
					return nil
				}
			}
			return err
		})
	}

	eg.Go(func() error {
{'fraction_non_alphanumeric': 0.14646230066573773, 'fraction_numerical': 0.0012385818238117355, 'mean_word_length': 3.671005061460593, 'pattern_counts': {'":': 0, '<': 3, '<?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': '4612160', 'n_tokens_mistral': 2693, 'n_tokens_neox': 2448, 'n_words': 569}