agent/install/service_linux.go (181 lines of code) (raw):
package install
import (
"github.com/aliyun/aliyun_assist_client/thirdparty/service"
"github.com/aliyun/aliyun_assist_client/agent/util/systemdutil"
)
const (
systemdScript = `[Unit]
Description={{.Description}}
ConditionFileIsExecutable={{.Path|cmdEscape}}
{{range $i, $dep := .Dependencies}}
{{$dep}} {{end}}
[Service]
StandardOutput=journal+console
StandardError=journal+console
StartLimitInterval=3600
StartLimitBurst=10
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}}
{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}}
{{if .UserName}}User={{.UserName}}{{end}}
{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}}
{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}}
{{if and .LogOutput .HasOutputFileSupport -}}
StandardOutput=file:/var/log/{{.Name}}.out
StandardError=file:/var/log/{{.Name}}.err
{{- end}}
{{if gt .LimitNOFILE -1 }}LimitNOFILE={{.LimitNOFILE}}{{end}}
{{if .Restart}}Restart={{.Restart}}{{end}}
{{if .SuccessExitStatus}}SuccessExitStatus={{.SuccessExitStatus}}{{end}}
RestartSec=120
EnvironmentFile=-/etc/sysconfig/{{.Name}}
KillMode=process
[Install]
WantedBy=multi-user.target
`
// NOTE: sysvinit script below is copied from https://github.com/kardianos/service/master/service_sysv_linux.go
// with modification:
// * In chkconfig configuration [default_runlevels start_priority stop_priority],
// runlevels to be started by default is modified which should be consistent
// with LSB init configuration below. And two priorities is also modified
// to be consistent with installing code in library.
// * "Required-Start" part of LSB init configuration is modified according to
// https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/facilname.html
// for service dependency ordering, like what systemd provides.
// See https://linux.die.net/man/8/chkconfig for more details
sysvScript = `#!/bin/sh
# For RedHat and cousins:
# chkconfig: 2345 50 02
# description: {{.Description}}
# processname: {{.Path}}
### BEGIN INIT INFO
# Provides: {{.Path}}
# Required-Start: $local_fs $network $named $remote_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: {{.DisplayName}}
# Description: {{.Description}}
### END INIT INFO
cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}"
name=$(basename $(readlink -f $0))
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
[ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps $(get_pid) > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
{{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}}
$cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill $(get_pid)
for i in $(seq 1 10)
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
`
upstartScript = `# {{.Description}}
{{if .DisplayName}}description "{{.DisplayName}}"{{end}}
{{if .HasKillStanza}}kill signal INT{{end}}
{{if .ChRoot}}chroot {{.ChRoot}}{{end}}
{{if .WorkingDirectory}}chdir {{.WorkingDirectory}}{{end}}
start on filesystem or runlevel [2345]
stop on runlevel [!2345]
{{if and .UserName .HasSetUIDStanza}}setuid {{.UserName}}{{end}}
respawn
respawn limit 3 120
normal exit 0
umask 022
console none
pre-start script
test -x {{.Path}} || { stop; exit 0; }
end script
# Start
script
{{if .LogOutput}}
stdout_log="/var/log/{{.Name}}.out"
stderr_log="/var/log/{{.Name}}.err"
{{end}}
if [ -f "/etc/sysconfig/{{.Name}}" ]; then
set -a
source /etc/sysconfig/{{.Name}}
set +a
fi
exec {{if and .UserName (not .HasSetUIDStanza)}}sudo -E -u {{.UserName}} {{end}}{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}{{if .LogOutput}} >> $stdout_log 2>> $stderr_log{{end}}
end script
`
)
func ServiceConfig() *service.Config {
ServiceName := ""
depends := []string{}
option := make(service.KeyValue)
if systemdutil.IsRunningSystemd() {
ServiceName = "aliyun"
// Official doc https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/
// suggests both After= and Wants= configuration to delay a service after
// network is up. Need validation on ALL distros and releases.
depends = append(depends, "After=network-online.target")
depends = append(depends, "Wants=network-online.target")
option["SystemdScript"] = systemdScript
option["Restart"] = "on-failure"
// REMEMBER: Explicit disable LogOutput option of kardianos/service, and
// use StandardOutput/StandardError settings manually written above.
option["LogOutput"] = false
} else {
ServiceName = "aliyun-service"
option["SysvScript"] = sysvScript
option["UpstartScript"] = upstartScript
option["LogOutput"] = true
}
return &service.Config{
// 服务显示名称
Name: ServiceName,
// 服务名称
DisplayName: "Aliyun Assist Service",
// 服务描述
Description: "Aliyun Assist",
// TODO: Use symbolic link for shorter path needs more adaption
// Executable: "/usr/sbin/aliyun-service",
Dependencies: depends, //云助手依赖网络
Option: option,
}
}