/
service.go
123 lines (109 loc) · 2.81 KB
/
service.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
// Package ubios implements the Unifi OS init system.
//
// On ubios the base system does not provide a hook to start custom services.
// Although, the base system starts a podman (Docker like) container with a
// standard Debian stretch with systemd.
//
// The trick is thus to install a systemd init that will start the service from
// inside the container.
package ubios
import (
"bufio"
"os"
"os/exec"
"strings"
"github.com/nextdns/nextdns/host/service"
"github.com/nextdns/nextdns/host/service/internal"
"github.com/nextdns/nextdns/host/service/systemd"
)
type Service struct {
systemd.Service
}
func isUnifi() bool {
if st, _ := os.Stat("/data/unifi"); st != nil && st.IsDir() {
return true
}
if err := exec.Command("ubnt-device-info", "firmware").Run(); err == nil {
return true
}
return false
}
func New(c service.Config) (Service, error) {
if !isUnifi() {
return Service{}, service.ErrNotSupported
}
srv := Service{
Service: systemd.Service{
Config: c,
ConfigFileStorer: service.ConfigFileStorer{File: "/data/" + c.Name + ".conf"},
Path: "/etc/systemd/system/" + c.Name + ".service",
},
}
if usePodman, _ := isContainerized(); usePodman {
srv.Config.Flags = append(srv.Config.Flags, "podman")
}
return srv, nil
}
func isContainerized() (bool, error) {
f, err := os.Open("/proc/1/cgroup")
if err != nil {
return false, err
}
defer f.Close()
s := bufio.NewScanner(f)
for s.Scan() {
flds := strings.Split(s.Text(), ":")
if len(flds) != 3 {
continue
}
if flds[2] != "/" && flds[2] != "/init.scope" {
return true, nil
}
}
return false, nil
}
func (s Service) Install() error {
if err := os.WriteFile("/data/nextdns", script, 0755); err != nil {
return err
}
if err := internal.CreateWithTemplate(s.Path, tmpl, 0644, s.Config); err != nil {
return err
}
if err := internal.Run("systemctl", "enable", s.Name+".service"); err != nil {
return err
}
return internal.Run("systemctl", "daemon-reload")
}
func (s Service) Uninstall() error {
os.Remove("/data/nextdns")
return s.Service.Uninstall()
}
var tmpl = `[Unit]
Description={{.Description}}
ConditionFileIsExecutable={{.Executable}}
After=unifi.service
Before=nss-lookup.target
Wants=nss-lookup.target
[Service]
StartLimitInterval=5
StartLimitBurst=10
Environment={{.RunModeEnv}}=1
ExecStart={{.Executable}}{{range .Arguments}} {{.}}{{end}}
{{- if (.Config.HasFlag "podman") }}
ExecStartPost=ssh -oStrictHostKeyChecking=no 127.0.0.1 ln -sf /data/nextdns /usr/bin/nextdns
{{- end}}
RestartSec=120
LimitMEMLOCK=infinity
[Install]
WantedBy=multi-user.target
`
var script = []byte(`#!/bin/sh
if [ "$(. /etc/os-release; echo "$ID")" = "ubios" ]; then
if [ "$1" = "upgrade" ]; then
RUN_COMMAND=upgrade sh -c "$(curl -s https://nextdns.io/install)"
fi
podman exec unifi-os nextdns "$@"
else
nextdns "$@"
fi
`)