-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathservice_unix.go
178 lines (147 loc) · 3.89 KB
/
service_unix.go
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//go:build !windows
// +build !windows
package service
import (
"fmt"
"os"
"strconv"
"gopkg.in/hlandau/service.v3/daemon"
"gopkg.in/hlandau/service.v3/daemon/bansuid"
"gopkg.in/hlandau/svcutils.v1/caps"
"gopkg.in/hlandau/svcutils.v1/passwd"
"gopkg.in/hlandau/svcutils.v1/pidfile"
"gopkg.in/hlandau/svcutils.v1/systemd"
)
// This will always point to a path which the platform guarantees is an empty
// directory. You can use it as your default chroot path if your service doesn't
// access the filesystem after it's started.
//
// On Linux, the FHS provides that "/var/empty" is an empty directory, so it
// points to that.
var EmptyChrootPath = daemon.EmptyChrootPath
func usingPlatform(platformName string) bool {
return platformName == "unix"
}
func systemdUpdateStatus(status string) error {
return systemd.NotifySend(status)
}
func (info *Info) serviceMain() error {
if info.Config.Fork {
isParent, err := daemon.Fork()
if err != nil {
return err
}
if isParent {
os.Exit(0)
}
info.Config.Daemon = true
}
err := daemon.Init()
if err != nil {
return err
}
err = systemdUpdateStatus("\n")
if err == nil {
info.systemd = true
}
// default: daemon=no, stderr=yes
// --daemon: daemon=yes, stderr=no
// systemd/--daemon --stderr: daemon=yes, stderr=yes
// systemd --daemon: daemon=yes, stderr=no
daemonize := info.Config.Daemon
keepStderr := info.Config.Stderr
if !daemonize && info.systemd {
daemonize = true
keepStderr = true
}
if daemonize {
err := daemon.Daemonize(keepStderr)
if err != nil {
return err
}
}
if info.Config.PIDFile != "" {
info.pidFileName = info.Config.PIDFile
err = info.openPIDFile()
if err != nil {
return err
}
defer info.closePIDFile()
}
return info.runInteractively()
}
func (info *Info) openPIDFile() error {
f, err := pidfile.Open(info.pidFileName)
info.pidFile = f
return err
}
func (info *Info) closePIDFile() {
if info.pidFile != nil {
info.pidFile.Close()
}
}
func (h *ihandler) DropPrivileges() error {
if h.dropped {
return nil
}
// Extras
if !h.info.NoBanSuid {
// Try and bansuid, but don't process errors. It may not be supported on
// the current platform, and Linux won't allow SECUREBITS to be set unless
// one is root (or has the right capability). This is basically a
// best-effort thing.
bansuid.BanSuid()
}
// Various fixups
if h.info.Config.UID != "" && h.info.Config.GID == "" {
gid, err := passwd.GetGIDForUID(h.info.Config.UID)
if err != nil {
return err
}
h.info.Config.GID = strconv.FormatInt(int64(gid), 10)
}
if h.info.DefaultChroot == "" {
h.info.DefaultChroot = "/"
}
chrootPath := h.info.Config.Chroot
if chrootPath == "" {
chrootPath = h.info.DefaultChroot
}
uid := -1
gid := -1
if h.info.Config.UID != "" {
var err error
uid, err = passwd.ParseUID(h.info.Config.UID)
if err != nil {
return err
}
gid, err = passwd.ParseGID(h.info.Config.GID)
if err != nil {
return err
}
}
if (uid <= 0) != (gid <= 0) {
return fmt.Errorf("Either both or neither of the UID and GID must be positive")
}
if uid > 0 {
chrootErr, err := daemon.DropPrivileges(uid, gid, chrootPath)
if err != nil {
return fmt.Errorf("Failed to drop privileges: %v", err)
}
if chrootErr != nil && h.info.Config.Chroot != "" && h.info.Config.Chroot != "/" {
return fmt.Errorf("Failed to chroot: %v", chrootErr)
}
} else if h.info.Config.Chroot != "" && h.info.Config.Chroot != "/" {
return fmt.Errorf("Must use privilege dropping to use chroot; set -uid")
}
// If we still have any caps (maybe because we didn't setuid), try and drop them.
err := caps.Drop()
if err != nil {
return fmt.Errorf("cannot drop caps: %v", err)
}
if !h.info.AllowRoot && daemon.IsRoot() {
return fmt.Errorf("Daemon must not run as root or with capabilities; run as non-root user or use -uid")
}
h.dropped = true
return nil
}