README.md
1# go-systemd
2
3[![Go Report Card](https://goreportcard.com/badge/github.com/iguanesolutions/go-systemd)](https://goreportcard.com/report/github.com/iguanesolutions/go-systemd) [![PkgGoDev](https://pkg.go.dev/badge/github.com/iguanesolutions/go-systemd/v5)](https://pkg.go.dev/github.com/iguanesolutions/go-systemd/v5)
4
5Easily communicate with systemd when run as daemon within a service unit.
6
7## Notifier
8
9[![PkgGoDev](https://pkg.go.dev/badge/github.com/iguanesolutions/go-systemd/v5/notify)](https://pkg.go.dev/github.com/iguanesolutions/go-systemd/v5/notify)
10
11With notifier you can notify to systemd that your program is starting, stopping, reloading...
12
13For example, if your daemon needs some time for initializing its controllers before really being considered as ready, you can specify to systemd that this is a "notify" service and send it a notification when ready.
14
15It is safe to use it even if systemd notify support is disabled (noop call).
16
17```systemdunit
18[Service]
19Type=notify
20```
21
22```go
23import (
24 sysdnotify "github.com/iguanesolutions/go-systemd/v5/notify"
25)
26
27// Init http server
28server := &http.Server{
29 Addr: "host:port",
30 Handler: myHTTPHandler,
31}
32
33/*
34 Do some more inits
35*/
36
37// Notify ready to systemd
38if err = sysdnotify.Ready(); err != nil {
39 log.Printf("failed to notify ready to systemd: %v\n", err)
40}
41
42// Start the server
43if err = server.ListenAndServe(); err != nil {
44 log.Printf("failed to start http server: %v\n", err)
45}
46```
47
48When stopping, you can notify systemd that you have indeed received the SIGTERM and you have launched the stop procedure
49
50```go
51import (
52 sysdnotify "github.com/iguanesolutions/go-systemd/v5/notify"
53)
54
55// Notify to systemd that we are stopping
56var err error
57if err = sysdnotify.Stopping(); err != nil {
58 log.Printf("failed to notify stopping to systemd: %v\n", err)
59}
60
61/*
62 Stop others things
63*/
64
65// Stop the server (with timeout)
66ctx, cancelCtx := context.WithTimeout(context.Background(), 5*time.Second)
67defer cancelCtx()
68if err = server.Shutdown(ctx); err != nil {
69 log.Printf("failed to shutdown http server: %v\n", err)
70}
71```
72
73You can also notify status to systemd
74
75```go
76import (
77 sysdnotify "github.com/iguanesolutions/go-systemd/v5/notify"
78)
79
80if err := sysdnotify.Status(fmt.Sprintf("There is currently %d active connections", activeConns)); err != nil {
81 log.Printf("failed to notify status to systemd: %v\n", err)
82}
83
84```
85
86systemctl status output example:
87
88```systemctlstatus
89user@host:~$ systemctl status superapp.service
90● superapp.service - superapp
91 Loaded: loaded (/lib/systemd/system/superapp.service; enabled)
92 Active: active (running) since Mon 2018-06-25 08:54:35 UTC; 3 days ago
93 Main PID: 2604 (superapp)
94 Status: "There is currently 1506 active connections"
95 ...
96```
97
98### Watchdog
99
100[![PkgGoDev](https://pkg.go.dev/badge/github.com/iguanesolutions/go-systemd/v5/notify/watchdog)](https://pkg.go.dev/github.com/iguanesolutions/go-systemd/v5/notify/watchdog)
101
102```systemdunit
103[Service]
104Type=notify
105WatchdogSec=30s
106```
107
108```go
109import (
110 sysdwatchdog "github.com/iguanesolutions/go-systemd/v5/notify/watchdog"
111)
112
113// Init systemd watchdog, same as the notifier, it can be nil if your os does not support it
114watchdog, err := sysdwatchdog.New()
115if err != nil {
116 log.Printf("failed to initialize systemd watchdog controller: %v\n", err)
117}
118
119if watchdog != nil {
120 // Then start a watcher worker
121 go func() {
122 ticker := watchdog.NewTicker()
123 defer ticker.Stop()
124 for {
125 select {
126 // Ticker chan
127 case <-ticker.C:
128 // Check if something wrong, if not send heartbeat
129 if allGood {
130 if err = watchdog.SendHeartbeat(); err != nil {
131 log.Printf("failed to send systemd watchdog heartbeat: %v\n", err)
132 }
133 }
134 // Some stop signal chan
135 case <-stopSig:
136 return
137 }
138 }
139 }()
140}
141```
142
143## Resolved
144
145[![PkgGoDev](https://pkg.go.dev/badge/github.com/iguanesolutions/go-systemd/resolved/resolved)](https://pkg.go.dev/github.com/iguanesolutions/go-systemd/v5/resolved)
146
147This package is still under development and very experimental, do not use it in production.
148We started this package in order to go deep into the DNS world. So we are opened to any suggestions/contributions on this.
149DNS is not trivial at all so there can be some stuff that are not rfc compliant (like sorting addresses etc...).
150
151The resolved package features:
152 * Pure Go implementation of `org.freedesktop.resolve1` dbus interface
153 * Resolver type (which uses the underlying dbus interface) that tries to implement the same methods as `net.Resolver` from Go standard library
154 * Unit tests (make sure Go resolver and systemd-resolved query the same dns server)
155
156### Dbus
157
158The following example shows how to use the resolve1 dbus connection to resolve an host:
159
160```go
161package main
162
163import (
164 "context"
165 "fmt"
166 "log"
167 "syscall"
168
169 "github.com/iguanesolutions/go-systemd/v5/resolved"
170)
171
172func main() {
173 c, err := resolved.NewConn()
174 if err != nil {
175 log.Fatal("ERROR: ", err)
176 }
177 ctx := context.Background()
178 addrs, canonical, flags, err := c.ResolveHostname(ctx, 0, "google.com", syscall.AF_UNSPEC, 0)
179 if err != nil {
180 log.Println("ERROR: ", err)
181 } else {
182 fmt.Println("Addresses: ", addrs)
183 fmt.Println("Canonical: ", canonical)
184 fmt.Println("OutputFlags: ", flags)
185 }
186 err = c.Close()
187 if err != nil {
188 log.Println("ERROR: ", err)
189 }
190}
191```
192
193Output:
194
195```output
196Addresses: [{
197 IfIndex: 2,
198 Family: 2,
199 IP: 142.250.74.238,
200} {
201 IfIndex: 2,
202 Family: 10,
203 IP: 2a00:1450:4007:80b::200e,
204}]
205Canonical: google.com
206Flags: 1
207```
208
209### Resolver
210
211The following example shows how to use the resolved Resolver to resolve an host:
212
213```go
214package main
215
216import (
217 "context"
218 "fmt"
219 "log"
220
221 "github.com/iguanesolutions/go-systemd/v5/resolved"
222)
223
224func main() {
225 r, err := resolved.NewResolver()
226 if err != nil {
227 log.Fatal("ERROR: ", err)
228 }
229 ctx := context.Background()
230 addrs, err := r.LookupHost(ctx, "google.com")
231 if err != nil {
232 log.Println("ERROR: ", err)
233 } else {
234 fmt.Println("Addresses: ", addrs)
235 }
236 err = r.Close()
237 if err != nil {
238 log.Println("ERROR: ", err)
239 }
240}
241```
242
243Output:
244
245```output
246Addresses: [2a00:1450:4007:80b::200e 142.250.74.238]
247```
248
249### HTTP Client
250
251The following example shows how to use the systemd-resolved Resolver with the Go http client from the standard library:
252
253```go
254package main
255
256import (
257 "fmt"
258 "log"
259 "net/http"
260
261 "github.com/iguanesolutions/go-systemd/v5/resolved"
262)
263
264func main() {
265 r, err := resolved.NewResolver()
266 if err != nil {
267 log.Fatal("ERROR: ", err)
268 }
269 // if you want to make a custom http client using systemd-resolved as resolver
270 httpCli := &http.Client{
271 Transport: &http.Transport{
272 DialContext: r.DialContext,
273 },
274 }
275 // or if you don't have an http client you can call HTTPClient method on resolver
276 // it comes with some nice default values.
277 httpCli = r.HTTPClient()
278 resp, err := httpCli.Get("https://google.com")
279 if err != nil {
280 log.Println("ERROR: ", err)
281 } else {
282 fmt.Println("Status: ", resp.Status)
283 err = resp.Body.Close()
284 if err != nil {
285 log.Println("ERROR: ", err)
286 }
287 }
288 err = r.Close()
289 if err != nil {
290 log.Println("ERROR: ", err)
291 }
292}
293```
294
295Output:
296
297```output
298Status: 200 OK
299```
300