1package runhcs
2
3import (
4	"context"
5	"fmt"
6	"path/filepath"
7	"strings"
8
9	irunhcs "github.com/Microsoft/hcsshim/internal/runhcs"
10	runc "github.com/containerd/go-runc"
11)
12
13// CreateOpts is set of options that can be used with the Create command.
14type CreateOpts struct {
15	runc.IO
16	// PidFile is the path to the file to write the process id to.
17	PidFile string
18	// ShimLog is the path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-<container-id>-shim-log) for the launched shim process.
19	ShimLog string
20	// VMLog is the path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-<container-id>-vm-log) for the launched VM shim process.
21	VMLog string
22	// VMConsole is the path to the pipe for the VM's console (e.g. \\.\pipe\debugpipe)
23	VMConsole string
24}
25
26func (opt *CreateOpts) args() ([]string, error) {
27	var out []string
28	if opt.PidFile != "" {
29		abs, err := filepath.Abs(opt.PidFile)
30		if err != nil {
31			return nil, err
32		}
33		out = append(out, "--pid-file", abs)
34	}
35	if opt.ShimLog != "" {
36		if strings.HasPrefix(opt.ShimLog, irunhcs.SafePipePrefix) {
37			out = append(out, "--shim-log", opt.ShimLog)
38		} else {
39			abs, err := filepath.Abs(opt.ShimLog)
40			if err != nil {
41				return nil, err
42			}
43			out = append(out, "--shim-log", abs)
44		}
45	}
46	if opt.VMLog != "" {
47		if strings.HasPrefix(opt.VMLog, irunhcs.SafePipePrefix) {
48			out = append(out, "--vm-log", opt.VMLog)
49		} else {
50			abs, err := filepath.Abs(opt.VMLog)
51			if err != nil {
52				return nil, err
53			}
54			out = append(out, "--vm-log", abs)
55		}
56	}
57	if opt.VMConsole != "" {
58		out = append(out, "--vm-console", opt.VMConsole)
59	}
60	return out, nil
61}
62
63// Create creates a new container and returns its pid if it was created
64// successfully.
65func (r *Runhcs) Create(context context.Context, id, bundle string, opts *CreateOpts) error {
66	args := []string{"create", "--bundle", bundle}
67	if opts != nil {
68		oargs, err := opts.args()
69		if err != nil {
70			return err
71		}
72		args = append(args, oargs...)
73	}
74	cmd := r.command(context, append(args, id)...)
75	if opts != nil && opts.IO != nil {
76		opts.Set(cmd)
77	}
78	if cmd.Stdout == nil && cmd.Stderr == nil {
79		data, err := cmdOutput(cmd, true)
80		if err != nil {
81			return fmt.Errorf("%s: %s", err, data)
82		}
83		return nil
84	}
85	ec, err := runc.Monitor.Start(cmd)
86	if err != nil {
87		return err
88	}
89	if opts != nil && opts.IO != nil {
90		if c, ok := opts.IO.(runc.StartCloser); ok {
91			if err := c.CloseAfterStart(); err != nil {
92				return err
93			}
94		}
95	}
96	status, err := runc.Monitor.Wait(cmd, ec)
97	if err == nil && status != 0 {
98		err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0])
99	}
100	return nil
101}
102