aboutsummaryrefslogtreecommitdiff
path: root/cmd/podman/system/service.go
blob: 99a6b1e1e910e6d0f966df531a2309a5e7291af8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// +build linux,!remote

package system

import (
	"net/url"
	"os"
	"path/filepath"
	"syscall"
	"time"

	"github.com/containers/common/pkg/completion"
	"github.com/containers/podman/v3/cmd/podman/common"
	"github.com/containers/podman/v3/cmd/podman/registry"
	"github.com/containers/podman/v3/pkg/domain/entities"
	"github.com/containers/podman/v3/pkg/rootless"
	"github.com/containers/podman/v3/pkg/systemd"
	"github.com/containers/podman/v3/pkg/util"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
)

var (
	srvDescription = `Run an API service

Enable a listening service for API access to Podman commands.
`

	srvCmd = &cobra.Command{
		Annotations:       map[string]string{registry.EngineMode: registry.ABIMode},
		Use:               "service [options] [URI]",
		Args:              cobra.MaximumNArgs(1),
		Short:             "Run API service",
		Long:              srvDescription,
		RunE:              service,
		ValidArgsFunction: common.AutocompleteDefaultOneArg,
		Example:           `podman system service --time=0 unix:///tmp/podman.sock`,
	}

	srvArgs = struct {
		Timeout     int64
		CorsHeaders string
	}{}
)

func init() {
	registry.Commands = append(registry.Commands, registry.CliCommand{
		Command: srvCmd,
		Parent:  systemCmd,
	})

	flags := srvCmd.Flags()

	cfg := registry.PodmanConfig()
	timeFlagName := "time"
	flags.Int64VarP(&srvArgs.Timeout, timeFlagName, "t", int64(cfg.Engine.ServiceTimeout), "Time until the service session expires in seconds.  Use 0 to disable the timeout")
	_ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
	flags.StringVarP(&srvArgs.CorsHeaders, "cors", "", "", "Set CORS Headers")
	_ = srvCmd.RegisterFlagCompletionFunc("cors", completion.AutocompleteNone)

	flags.SetNormalizeFunc(aliasTimeoutFlag)
}

func aliasTimeoutFlag(_ *pflag.FlagSet, name string) pflag.NormalizedName {
	if name == "timeout" {
		name = "time"
	}
	return pflag.NormalizedName(name)
}

func service(cmd *cobra.Command, args []string) error {
	apiURI, err := resolveAPIURI(args)
	if err != nil {
		return err
	}
	logrus.Infof("Using API endpoint: '%s'", apiURI)
	// Clean up any old existing unix domain socket
	if len(apiURI) > 0 {
		uri, err := url.Parse(apiURI)
		if err != nil {
			return err
		}

		// socket activation uses a unix:// socket in the shipped unit files but apiURI is coded as "" at this layer.
		if uri.Scheme == "unix" && !registry.IsRemote() {
			if err := syscall.Unlink(uri.Path); err != nil && !os.IsNotExist(err) {
				return err
			}
			mask := syscall.Umask(0177)
			defer syscall.Umask(mask)
		}
	}

	opts := entities.ServiceOptions{
		URI:         apiURI,
		Command:     cmd,
		CorsHeaders: srvArgs.CorsHeaders,
	}

	opts.Timeout = time.Duration(srvArgs.Timeout) * time.Second
	return restService(opts, cmd.Flags(), registry.PodmanConfig())
}

func resolveAPIURI(_url []string) (string, error) {
	// When determining _*THE*_ listening endpoint --
	// 1) User input wins always
	// 2) systemd socket activation
	// 3) rootless honors XDG_RUNTIME_DIR
	// 4) lastly adapter.DefaultAPIAddress

	if len(_url) == 0 {
		if v, found := os.LookupEnv("PODMAN_SOCKET"); found {
			logrus.Debugf("PODMAN_SOCKET='%s' used to determine API endpoint", v)
			_url = []string{v}
		}
	}

	switch {
	case len(_url) > 0 && _url[0] != "":
		return _url[0], nil
	case systemd.SocketActivated():
		logrus.Info("Using systemd socket activation to determine API endpoint")
		return "", nil
	case rootless.IsRootless():
		xdg, err := util.GetRuntimeDir()
		if err != nil {
			return "", err
		}

		socketName := "podman.sock"
		socketPath := filepath.Join(xdg, "podman", socketName)
		if err := os.MkdirAll(filepath.Dir(socketPath), 0700); err != nil {
			return "", err
		}
		return "unix:" + socketPath, nil
	default:
		if err := os.MkdirAll(filepath.Dir(registry.DefaultRootAPIPath), 0700); err != nil {
			return "", err
		}
		return registry.DefaultRootAPIAddress, nil
	}
}