Skip to content

Commit ce54a94

Browse files
committed
Pull request 2504: AGDNS-2966-copy-service-code
Squashed commit of the following: commit cb943be Merge: 279ed98 cf32802 Author: Eugene Burkov <[email protected]> Date: Tue Nov 11 15:50:19 2025 +0300 Merge branch 'master' into AGDNS-2966-copy-service-code commit 279ed98 Author: Eugene Burkov <[email protected]> Date: Tue Nov 11 13:37:16 2025 +0300 ossvc: imp docs commit e6eddae Author: Eugene Burkov <[email protected]> Date: Fri Nov 7 19:46:37 2025 +0300 all: move scripts into os-specific files commit ecec7a2 Author: Eugene Burkov <[email protected]> Date: Mon Oct 20 15:31:25 2025 +0300 ossvc: move some code from home
1 parent cf32802 commit ce54a94

File tree

10 files changed

+1241
-35
lines changed

10 files changed

+1241
-35
lines changed

internal/ossvc/config.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package ossvc
2+
3+
import (
4+
"fmt"
5+
"time"
6+
7+
"github.com/kardianos/service"
8+
)
9+
10+
// configureServiceOptions defines additional settings of the service
11+
// configuration. conf must not be nil.
12+
//
13+
//lint:ignore U1000 TODO(e.burkov): Use.
14+
func configureServiceOptions(conf *service.Config, versionInfo string) {
15+
conf.Option["SvcInfo"] = fmt.Sprintf("%s %s", versionInfo, time.Now())
16+
17+
configureOSOptions(conf)
18+
}

internal/ossvc/config_darwin.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//go:build darwin
2+
3+
package ossvc
4+
5+
import (
6+
"github.com/kardianos/service"
7+
)
8+
9+
// configureServiceOptions defines additional settings of the service
10+
// configuration on macOS. conf must not be nil.
11+
func configureOSOptions(conf *service.Config) {
12+
conf.Option["LaunchdConfig"] = launchdConfig
13+
}
14+
15+
// launchdConfig is the template for Launchd service file. Basically the same
16+
// template as the one defined in github.com/kardianos/service but with two
17+
// additional keys:
18+
// - StandardOutPath
19+
// - StandardErrorPath
20+
//
21+
//lint:ignore U1000 TODO(e.burkov): Use.
22+
const launchdConfig = `<?xml version='1.0' encoding='UTF-8'?>
23+
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
24+
"http://www.apple.com/DTDs/PropertyList-1.0.dtd" >
25+
<plist version='1.0'>
26+
<dict>
27+
<key>Label</key><string>{{html .Name}}</string>
28+
<key>ProgramArguments</key>
29+
<array>
30+
<string>{{html .Path}}</string>
31+
{{range .Config.Arguments}}
32+
<string>{{html .}}</string>
33+
{{end}}
34+
</array>
35+
{{if .UserName}}<key>UserName</key><string>{{html .UserName}}</string>{{end}}
36+
{{if .ChRoot}}<key>RootDirectory</key><string>{{html .ChRoot}}</string>{{end}}
37+
{{if .WorkingDirectory}}<key>WorkingDirectory</key><string>{{html .WorkingDirectory}}</string>{{end}}
38+
<key>SessionCreate</key><{{bool .SessionCreate}}/>
39+
<key>KeepAlive</key><{{bool .KeepAlive}}/>
40+
<key>RunAtLoad</key><{{bool .RunAtLoad}}/>
41+
<key>Disabled</key><false/>
42+
<key>StandardOutPath</key>
43+
<string>` + launchdStdoutPath + `</string>
44+
<key>StandardErrorPath</key>
45+
<string>` + launchdStderrPath + `</string>
46+
</dict>
47+
</plist>
48+
`

internal/ossvc/config_freebsd.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//go:build freebsd
2+
3+
package ossvc
4+
5+
import (
6+
"github.com/kardianos/service"
7+
)
8+
9+
// configureServiceOptions defines additional settings of the service
10+
// configuration on FreeBSD. conf must not be nil.
11+
func configureOSOptions(conf *service.Config) {
12+
conf.Option["SysvScript"] = freeBSDScript
13+
}
14+
15+
// freeBSDScript is the source of the daemon script for FreeBSD. Keep as close
16+
// as possible to the https://github.com/kardianos/service/blob/18c957a3dc1120a2efe77beb401d476bade9e577/service_freebsd.go#L204.
17+
//
18+
//lint:ignore U1000 TODO(e.burkov): Use.
19+
const freeBSDScript = `#!/bin/sh
20+
# PROVIDE: {{.Name}}
21+
# REQUIRE: networking
22+
# KEYWORD: shutdown
23+
24+
. /etc/rc.subr
25+
26+
name="{{.Name}}"
27+
{{.Name}}_env="IS_DAEMON=1"
28+
{{.Name}}_user="root"
29+
pidfile_child="/var/run/${name}.pid"
30+
pidfile="/var/run/${name}_daemon.pid"
31+
command="/usr/sbin/daemon"
32+
daemon_args="-P ${pidfile} -p ${pidfile_child} -r -t ${name}"
33+
command_args="${daemon_args} {{.Path}}{{range .Arguments}} {{.}}{{end}}"
34+
35+
run_rc_command "$1"
36+
`

internal/ossvc/config_linux.go

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
//go:build linux
2+
3+
package ossvc
4+
5+
import (
6+
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
7+
"github.com/kardianos/service"
8+
)
9+
10+
// configureServiceOptions defines additional settings of the service
11+
// configuration on Linux. conf must not be nil.
12+
func configureOSOptions(conf *service.Config) {
13+
conf.Option["LogOutput"] = true
14+
conf.Option["Restart"] = "always"
15+
16+
conf.Dependencies = []string{
17+
"After=syslog.target network-online.target",
18+
}
19+
20+
conf.Option["SystemdScript"] = systemdScript
21+
22+
// Use different scripts on OpenWrt.
23+
if aghos.IsOpenWrt() {
24+
conf.Option["SysvScript"] = openWrtScript
25+
} else {
26+
conf.Option["SysvScript"] = sysvScript
27+
}
28+
}
29+
30+
// systemdScript is an improved version of the systemd script originally from
31+
// the systemdScript constant in file service_systemd_linux.go in module
32+
// github.com/kardianos/service. The following changes have been made:
33+
//
34+
// 1. The RestartSec setting is set to a lower value of 10 to make sure we
35+
// always restart quickly.
36+
//
37+
// 2. The StandardOutput and StandardError settings are set to redirect the
38+
// output to the systemd journal, see
39+
// https://man7.org/linux/man-pages/man5/systemd.exec.5.html#LOGGING_AND_STANDARD_INPUT/OUTPUT.
40+
//
41+
//lint:ignore U1000 TODO(e.burkov): Use.
42+
const systemdScript = `[Unit]
43+
Description={{.Description}}
44+
ConditionFileIsExecutable={{.Path|cmdEscape}}
45+
{{range $i, $dep := .Dependencies}}
46+
{{$dep}} {{end}}
47+
48+
[Service]
49+
StartLimitInterval=5
50+
StartLimitBurst=10
51+
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
52+
{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}}
53+
{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}}
54+
{{if .UserName}}User={{.UserName}}{{end}}
55+
{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}}
56+
{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}}
57+
{{if and .LogOutput .HasOutputFileSupport -}}
58+
StandardOutput=journal
59+
StandardError=journal
60+
{{- end}}
61+
{{if gt .LimitNOFILE -1 }}LimitNOFILE={{.LimitNOFILE}}{{end}}
62+
{{if .Restart}}Restart={{.Restart}}{{end}}
63+
{{if .SuccessExitStatus}}SuccessExitStatus={{.SuccessExitStatus}}{{end}}
64+
RestartSec=10
65+
EnvironmentFile=-/etc/sysconfig/{{.Name}}
66+
67+
[Install]
68+
WantedBy=multi-user.target
69+
`
70+
71+
// sysvScript is the source of the daemon script for SysV-based Linux systems.
72+
// Keep as close as possible to the https://github.com/kardianos/service/blob/29f8c79c511bc18422bb99992779f96e6bc33921/service_sysv_linux.go#L187.
73+
//
74+
// Use ps command instead of reading the procfs since it's a more
75+
// implementation-independent approach.
76+
//
77+
//lint:ignore U1000 TODO(e.burkov): Use.
78+
const sysvScript = `#!/bin/sh
79+
# For RedHat and cousins:
80+
# chkconfig: - 99 01
81+
# description: {{.Description}}
82+
# processname: {{.Path}}
83+
84+
### BEGIN INIT INFO
85+
# Provides: {{.Path}}
86+
# Required-Start:
87+
# Required-Stop:
88+
# Default-Start: 2 3 4 5
89+
# Default-Stop: 0 1 6
90+
# Short-Description: {{.DisplayName}}
91+
# Description: {{.Description}}
92+
### END INIT INFO
93+
94+
cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}"
95+
96+
name=$(basename $(readlink -f $0))
97+
pid_file="/var/run/$name.pid"
98+
stdout_log="/var/log/$name.log"
99+
stderr_log="/var/log/$name.err"
100+
101+
[ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name
102+
103+
get_pid() {
104+
cat "$pid_file"
105+
}
106+
107+
is_running() {
108+
[ -f "$pid_file" ] && ps -p "$(get_pid)" > /dev/null 2>&1
109+
}
110+
111+
case "$1" in
112+
start)
113+
if is_running; then
114+
echo "Already started"
115+
else
116+
echo "Starting $name"
117+
{{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}}
118+
$cmd >> "$stdout_log" 2>> "$stderr_log" &
119+
echo $! > "$pid_file"
120+
if ! is_running; then
121+
echo "Unable to start, see $stdout_log and $stderr_log"
122+
exit 1
123+
fi
124+
fi
125+
;;
126+
stop)
127+
if is_running; then
128+
echo -n "Stopping $name.."
129+
kill $(get_pid)
130+
for i in $(seq 1 10)
131+
do
132+
if ! is_running; then
133+
break
134+
fi
135+
echo -n "."
136+
sleep 1
137+
done
138+
echo
139+
if is_running; then
140+
echo "Not stopped; may still be shutting down or shutdown may have failed"
141+
exit 1
142+
else
143+
echo "Stopped"
144+
if [ -f "$pid_file" ]; then
145+
rm "$pid_file"
146+
fi
147+
fi
148+
else
149+
echo "Not running"
150+
fi
151+
;;
152+
restart)
153+
$0 stop
154+
if is_running; then
155+
echo "Unable to stop, will not attempt to start"
156+
exit 1
157+
fi
158+
$0 start
159+
;;
160+
status)
161+
if is_running; then
162+
echo "Running"
163+
else
164+
echo "Stopped"
165+
exit 1
166+
fi
167+
;;
168+
*)
169+
echo "Usage: $0 {start|stop|restart|status}"
170+
exit 1
171+
;;
172+
esac
173+
exit 0
174+
`
175+
176+
// OpenWrt procd init script
177+
// https://github.com/AdguardTeam/AdGuardHome/internal/issues/1386
178+
//
179+
//lint:ignore U1000 TODO(e.burkov): Use.
180+
const openWrtScript = `#!/bin/sh /etc/rc.common
181+
182+
USE_PROCD=1
183+
184+
START=95
185+
STOP=01
186+
187+
cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}"
188+
name="{{.Name}}"
189+
pid_file="/var/run/${name}.pid"
190+
191+
start_service() {
192+
echo "Starting ${name}"
193+
194+
procd_open_instance
195+
procd_set_param command ${cmd}
196+
procd_set_param respawn # respawn automatically if something died
197+
procd_set_param stdout 1 # forward stdout of the command to logd
198+
procd_set_param stderr 1 # same for stderr
199+
procd_set_param pidfile ${pid_file} # write a pid file on instance start and remove it on stop
200+
201+
procd_close_instance
202+
echo "${name} has been started"
203+
}
204+
205+
stop_service() {
206+
echo "Stopping ${name}"
207+
}
208+
209+
EXTRA_COMMANDS="status"
210+
EXTRA_HELP=" status Print the service status"
211+
212+
get_pid() {
213+
cat "${pid_file}"
214+
}
215+
216+
is_running() {
217+
[ -f "${pid_file}" ] && ps | grep -v grep | grep $(get_pid) >/dev/null 2>&1
218+
}
219+
220+
status() {
221+
if is_running; then
222+
echo "Running"
223+
else
224+
echo "Stopped"
225+
exit 1
226+
fi
227+
}
228+
`

internal/ossvc/config_openbsd.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//go:build openbsd
2+
3+
package ossvc
4+
5+
import (
6+
"github.com/kardianos/service"
7+
)
8+
9+
// configureServiceOptions defines additional settings of the service
10+
// configuration on OpenBSD. conf must not be nil.
11+
func configureOSOptions(conf *service.Config) {
12+
conf.Option["RunComScript"] = openBSDScript
13+
}
14+
15+
// openBSDScript is the source of the daemon script for OpenBSD.
16+
//
17+
//lint:ignore U1000 TODO(e.burkov): Use.
18+
const openBSDScript = `#!/bin/ksh
19+
#
20+
# $OpenBSD: {{ .SvcInfo }}
21+
22+
daemon="{{.Path}}"
23+
daemon_flags={{ .Arguments | args }}
24+
daemon_logger="daemon.info"
25+
26+
. /etc/rc.d/rc.subr
27+
28+
rc_bg=YES
29+
30+
rc_cmd $1
31+
`

internal/ossvc/config_windows.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build windows
2+
3+
package ossvc
4+
5+
import "github.com/kardianos/service"
6+
7+
// configureOSOptions defines additional settings of the service
8+
// configuration on Windows.
9+
func configureOSOptions(_ *service.Config) {}

0 commit comments

Comments
 (0)