aboutsummaryrefslogtreecommitdiff
path: root/cmd/podman/system/service.go
blob: 1a93b3137e4edc8154b6911e4b6034abef0a4b70 (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
144
145
146
147
148
149
//go:build linux && !remote
// +build linux,!remote

package system

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

	"github.com/containers/common/pkg/completion"
	"github.com/containers/podman/v4/cmd/podman/common"
	"github.com/containers/podman/v4/cmd/podman/registry"
	"github.com/containers/podman/v4/pkg/domain/entities"
	"github.com/containers/podman/v4/pkg/rootless"
	"github.com/containers/podman/v4/pkg/systemd"
	"github.com/containers/podman/v4/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
  podman system service --time=0 tcp://localhost:8888`,
	}

	srvArgs = struct {
		CorsHeaders string
		PProfAddr   string
		Timeout     uint
	}{}
)

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

	flags := srvCmd.Flags()
	cfg := registry.PodmanConfig()

	timeFlagName := "time"
	flags.UintVarP(&srvArgs.Timeout, timeFlagName, "t", cfg.Engine.ServiceTimeout,
		"Time until the service session expires in seconds.  Use 0 to disable the timeout")
	_ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
	flags.SetNormalizeFunc(aliasTimeoutFlag)

	flags.StringVarP(&srvArgs.CorsHeaders, "cors", "", "", "Set CORS Headers")
	_ = srvCmd.RegisterFlagCompletionFunc("cors", completion.AutocompleteNone)

	flags.StringVarP(&srvArgs.PProfAddr, "pprof-address", "", "",
		"Binding network address for pprof profile endpoints, default: do not expose endpoints")
	_ = flags.MarkHidden("pprof-address")
}

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
	}

	// 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)
		}
	}

	return restService(cmd.Flags(), registry.PodmanConfig(), entities.ServiceOptions{
		CorsHeaders: srvArgs.CorsHeaders,
		PProfAddr:   srvArgs.PProfAddr,
		Timeout:     time.Duration(srvArgs.Timeout) * time.Second,
		URI:         apiURI,
	})
}

func resolveAPIURI(uri []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(uri) == 0 {
		if v, found := os.LookupEnv("PODMAN_SOCKET"); found {
			logrus.Debugf("PODMAN_SOCKET=%q used to determine API endpoint", v)
			uri = []string{v}
		}
	}

	switch {
	case len(uri) > 0 && uri[0] != "":
		return uri[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
	}
}