1// +build !windows
2
3/*
4   Copyright The containerd Authors.
5
6   Licensed under the Apache License, Version 2.0 (the "License");
7   you may not use this file except in compliance with the License.
8   You may obtain a copy of the License at
9
10       http://www.apache.org/licenses/LICENSE-2.0
11
12   Unless required by applicable law or agreed to in writing, software
13   distributed under the License is distributed on an "AS IS" BASIS,
14   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   See the License for the specific language governing permissions and
16   limitations under the License.
17*/
18
19package tasks
20
21import (
22	gocontext "context"
23	"net/url"
24	"os"
25	"os/signal"
26
27	"github.com/containerd/console"
28	"github.com/containerd/containerd"
29	"github.com/containerd/containerd/cio"
30	"github.com/containerd/containerd/log"
31	"github.com/pkg/errors"
32	"github.com/urfave/cli"
33	"golang.org/x/sys/unix"
34)
35
36func init() {
37	startCommand.Flags = append(startCommand.Flags, cli.BoolFlag{
38		Name:  "no-pivot",
39		Usage: "disable use of pivot-root (linux only)",
40	})
41}
42
43// HandleConsoleResize resizes the console
44func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Console) error {
45	// do an initial resize of the console
46	size, err := con.Size()
47	if err != nil {
48		return err
49	}
50	if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
51		log.G(ctx).WithError(err).Error("resize pty")
52	}
53	s := make(chan os.Signal, 16)
54	signal.Notify(s, unix.SIGWINCH)
55	go func() {
56		for range s {
57			size, err := con.Size()
58			if err != nil {
59				log.G(ctx).WithError(err).Error("get pty size")
60				continue
61			}
62			if err := task.Resize(ctx, uint32(size.Width), uint32(size.Height)); err != nil {
63				log.G(ctx).WithError(err).Error("resize pty")
64			}
65		}
66	}()
67	return nil
68}
69
70// NewTask creates a new task
71func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, con console.Console, nullIO bool, logURI string, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) {
72	stdinC := &stdinCloser{
73		stdin: os.Stdin,
74	}
75	stdio := cio.NewCreator(append([]cio.Opt{cio.WithStreams(stdinC, os.Stdout, os.Stderr)}, ioOpts...)...)
76	if checkpoint != "" {
77		im, err := client.GetImage(ctx, checkpoint)
78		if err != nil {
79			return nil, err
80		}
81		opts = append(opts, containerd.WithTaskCheckpoint(im))
82	}
83	ioCreator := stdio
84	if con != nil {
85		ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(con, con, nil), cio.WithTerminal}, ioOpts...)...)
86	}
87	if nullIO {
88		if con != nil {
89			return nil, errors.New("tty and null-io cannot be used together")
90		}
91		ioCreator = cio.NullIO
92	}
93	if logURI != "" {
94		u, err := url.Parse(logURI)
95		if err != nil {
96			return nil, err
97		}
98		ioCreator = cio.LogURI(u)
99	}
100	t, err := container.NewTask(ctx, ioCreator, opts...)
101	if err != nil {
102		return nil, err
103	}
104	stdinC.closer = func() {
105		t.CloseIO(ctx, containerd.WithStdinCloser)
106	}
107	return t, nil
108}
109
110func getNewTaskOpts(context *cli.Context) []containerd.NewTaskOpts {
111	if context.Bool("no-pivot") {
112		return []containerd.NewTaskOpts{containerd.WithNoPivotRoot}
113	}
114	return nil
115}
116