aboutsummaryrefslogtreecommitdiff
path: root/pkg/systemd/generate/common.go
blob: bbd1a5f9253666d1c195167bc448112dde884bf6 (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
package generate

import (
	"strconv"
	"strings"

	"github.com/pkg/errors"
)

// EnvVariable "PODMAN_SYSTEMD_UNIT" is set in all generated systemd units and
// is set to the unit's (unique) name.
const EnvVariable = "PODMAN_SYSTEMD_UNIT"

// minTimeoutStopSec is the minimal stop timeout for generated systemd units.
// Once exceeded, processes of the services are killed and the cgroup(s) are
// cleaned up.
const minTimeoutStopSec = 60

// RestartPolicies includes all valid restart policies to be used in a unit
// file.
var RestartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"}

// validateRestartPolicy checks that the user-provided policy is valid.
func validateRestartPolicy(restart string) error {
	for _, i := range RestartPolicies {
		if i == restart {
			return nil
		}
	}
	return errors.Errorf("%s is not a valid restart policy", restart)
}

const headerTemplate = `# {{{{.ServiceName}}}}.service
{{{{- if (eq .GenerateNoHeader false) }}}}
# autogenerated by Podman {{{{.PodmanVersion}}}}
{{{{- if .TimeStamp}}}}
# {{{{.TimeStamp}}}}
{{{{- end}}}}
{{{{- end}}}}

[Unit]
Description=Podman {{{{.ServiceName}}}}.service
Documentation=man:podman-generate-systemd(1)
Wants=network.target
After=network-online.target
`

// filterPodFlags removes --pod and --pod-id-file from the specified command.
func filterPodFlags(command []string) []string {
	processed := []string{}
	for i := 0; i < len(command); i++ {
		s := command[i]
		if s == "--pod" || s == "--pod-id-file" {
			i++
			continue
		}
		if strings.HasPrefix(s, "--pod=") || strings.HasPrefix(s, "--pod-id-file=") {
			continue
		}
		processed = append(processed, s)
	}
	return processed
}

// escapeSystemdArguments makes sure that all arguments with at least one whitespace
// are quoted to make sure those are interpreted as one argument instead of
// multiple ones. Also make sure to escape all characters which have a special
// meaning to systemd -> $,% and \
// see: https://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines
func escapeSystemdArguments(command []string) []string {
	for i := range command {
		command[i] = strings.ReplaceAll(command[i], "$", "$$")
		command[i] = strings.ReplaceAll(command[i], "%", "%%")
		if strings.ContainsAny(command[i], " \t") {
			command[i] = strconv.Quote(command[i])
		} else if strings.Contains(command[i], `\`) {
			// strconv.Quote also escapes backslashes so
			// we should replace only if strconv.Quote was not used
			command[i] = strings.ReplaceAll(command[i], `\`, `\\`)
		}
	}
	return command
}

func removeDetachArg(args []string, argCount int) []string {
	// "--detach=false" could also be in the container entrypoint
	// split them off so we do not remove it there
	realArgs := args[len(args)-argCount:]
	flagArgs := removeArg("-d=false", args[:len(args)-argCount])
	flagArgs = removeArg("--detach=false", flagArgs)
	return append(flagArgs, realArgs...)
}

func removeReplaceArg(args []string, argCount int) []string {
	// "--replace=false" could also be in the container entrypoint
	// split them off so we do not remove it there
	realArgs := args[len(args)-argCount:]
	flagArgs := removeArg("--replace=false", args[:len(args)-argCount])
	return append(flagArgs, realArgs...)
}

func removeArg(arg string, args []string) []string {
	newArgs := []string{}
	for _, a := range args {
		if a != arg {
			newArgs = append(newArgs, a)
		}
	}
	return newArgs
}