1
+#!/usr/bin/env sh
2
+
3
+# SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+# Handle installation of the Netdata agent as a system service.
6
+#
7
+# Exit codes:
8
+# 0 - Successfully installed service.
9
+# 1 - Invalid arguments or other internal error.
10
+# 2 - Unable to detect system service type.
11
+# 3 - Detected system service type, but type not supported.
12
+# 4 - Detected system service type, but could not install due to other issues.
13
+# 5 - Platform not supported.
14
+
15
+set -e
16
+
17
+SCRIPT_SOURCE="$(
18
+ self=${0}
19
+ while [ -L "${self}" ]
20
+ do
21
+ cd "${self%/*}" || exit 1
22
+ self=$(readlink "${self}")
23
+ done
24
+ cd "${self%/*}" || exit 1
25
+ echo "$(pwd -P)/${self##*/}"
26
+)"
27
+
28
+DUMP_CMDS=0
29
+ENABLE="auto"
30
+EXPORT_CMDS=0
31
+INSTALL=1
32
+LINUX_INIT_TYPES="wsl systemd openrc lsb initd runit"
33
+PLATFORM="$(uname -s)"
34
+SVC_SOURCE="@libsysdir_POST@"
35
+SVC_TYPE="detect"
36
+
37
+# =====================================================================
38
+# Utility functions
39
+
40
+cleanup() {
41
+ ec="${?}"
42
+
43
+ if [ -n "${NETDATA_SAVE_WARNINGS}" ]; then
44
+ if [ -n "${NETDATA_PROPAGATE_WARNINGS}" ]; then
45
+ export NETDATA_WARNINGS="${NETDATA_WARNINGS}${SAVED_WARNINGS}"
46
+ fi
47
+ fi
48
+
49
+ trap - EXIT
50
+
51
+ exit "${ec}"
52
+}
53
+
54
+info() {
55
+ printf >&2 "%s\n" "${*}"
56
+}
57
+
58
+warning() {
59
+ if [ -n "${NETDATA_SAVE_WARNINGS}" ]; then
60
+ SAVED_WARNINGS="${SAVED_WARNINGS}\n - ${*}"
61
+ fi
62
+ printf >&2 "WARNING: %s\n" "${*}"
63
+}
64
+
65
+error() {
66
+ if [ -n "${NETDATA_SAVE_WARNINGS}" ]; then
67
+ SAVED_WARNINGS="${SAVED_WARNINGS}\n - ${*}"
68
+ fi
69
+ printf >&2 "ERROR: %s\n" "${*}"
70
+}
71
+
72
+get_os_key() {
73
+ if [ -f /etc/os-release ]; then
74
+ # shellcheck disable=SC1091
75
+ . /etc/os-release || return 1
76
+ echo "${ID}-${VERSION_ID}"
77
+
78
+ elif [ -f /etc/redhat-release ]; then
79
+ cat /etc/redhat-release
80
+ else
81
+ echo "unknown"
82
+ fi
83
+}
84
+
85
+valid_types() {
86
+ case "${PLATFORM}" in
87
+ Linux)
88
+ echo "detect systemd openrc lsb initd"
89
+ ;;
90
+ FreeBSD)
91
+ echo "detect freebsd"
92
+ ;;
93
+ Darwin)
94
+ echo "detect launchd"
95
+ ;;
96
+ *)
97
+ echo "detect"
98
+ ;;
99
+ esac
100
+}
101
+
102
+install_generic_service() {
103
+ svc_type="${1}"
104
+ svc_type_name="${2}"
105
+ svc_file="${3}"
106
+ svc_enable_hook="${4}"
107
+ svc_disable_hook="${5}"
108
+
109
+ info "Installing ${svc_type_name} service file."
110
+ if [ ! -f "${svc_file}" ] && [ "${ENABLE}" = "auto" ]; then
111
+ ENABLE="enable"
112
+ fi
113
+
114
+ if ! install -p -m 0755 -o 0 -g 0 "${SVC_SOURCE}/netdata-${svc_type}" /etc/init.d/netdata; then
115
+ error "Failed to install service file."
116
+ exit 4
117
+ fi
118
+
119
+ case "${ENABLE}" in
120
+ auto) true ;;
121
+ disable)
122
+ info "Disabling Netdata service."
123
+ ${svc_disable_hook}
124
+ ;;
125
+ enable)
126
+ info "Enabling Netdata service."
127
+ ${svc_enable_hook}
128
+ ;;
129
+ esac
130
+}
131
+
132
+dump_cmds() {
133
+ [ -n "${NETDATA_START_CMD}" ] && echo "NETDATA_START_CMD='${NETDATA_START_CMD}'"
134
+ [ -n "${NETDATA_STOP_CMD}" ] && echo "NETDATA_STOP_CMD='${NETDATA_STOP_CMD}'"
135
+ [ -n "${NETDATA_INSTALLER_START_CMD}" ] && echo "NETDATA_INSTALLER_START_CMD='${NETDATA_INSTALLER_START_CMD}'"
136
+ return 0
137
+}
138
+
139
+export_cmds() {
140
+ [ -n "${NETDATA_START_CMD}" ] && export NETDATA_START_CMD="${NETDATA_START_CMD}"
141
+ [ -n "${NETDATA_STOP_CMD}" ] && export NETDATA_STOP_CMD="${NETDATA_STOP_CMD}"
142
+ [ -n "${NETDATA_INSTALLER_START_CMD}" ] && export NETDATA_INSTALLER_START_CMD="${NETDATA_INSTALLER_START_COMMAND}"
143
+ return 0
144
+}
145
+
146
+save_cmds() {
147
+ dump_cmds > "${SAVE_CMDS_PATH}"
148
+}
149
+
150
+# =====================================================================
151
+# Help functions
152
+
153
+usage() {
154
+ cat << HEREDOC
155
+USAGE: install-service.sh [options]
156
+ where options include:
157
+
158
+ --source Specify where to find the service files to install (default ${SVC_SOURCE}).
159
+ --type Specify the type of service file to install. Specify a type of 'help' to get a list of valid types for your platform.
160
+ --cmds Additionally print a list of commands for starting and stopping the agent with the detected service type.
161
+ --export-cmds Export the variables that would be printed by the --cmds option.
162
+ --cmds-only Don't install, just handle the --cmds or --export-cmds option.
163
+ --enable Explicitly enable the service on install (default is to enable if not already installed).
164
+ --disable Explicitly disable the service on install.
165
+ --help Print this help information.
166
+HEREDOC
167
+}
168
+
169
+help_types() {
170
+ cat << HEREDOC
171
+Valid service types for ${PLATFORM} are:
172
+$(valid_types)
173
+HEREDOC
174
+}
175
+
176
+# =====================================================================
177
+# systemd support functions
178
+
179
+issystemd() {
180
+ pids=''
181
+ p=''
182
+ myns=''
183
+ ns=''
184
+ systemctl=''
185
+
186
+ # if the directory /lib/systemd/system OR /usr/lib/systemd/system (SLES 12.x) does not exit, it is not systemd
187
+ if [ ! -d /lib/systemd/system ] && [ ! -d /usr/lib/systemd/system ]; then
188
+ return 1
189
+ fi
190
+
191
+ # if there is no systemctl command, it is not systemd
192
+ systemctl=$(command -v systemctl 2> /dev/null)
193
+ if [ -z "${systemctl}" ] || [ ! -x "${systemctl}" ]; then
194
+ return 1
195
+ fi
196
+
197
+ # if pid 1 is systemd, it is systemd
198
+ [ "$(basename "$(readlink /proc/1/exe)" 2> /dev/null)" = "systemd" ] && return 0
199
+
200
+ # if systemd is not running, it is not systemd
201
+ pids=$(safe_pidof systemd 2> /dev/null)
202
+ [ -z "${pids}" ] && return 1
203
+
204
+ # check if the running systemd processes are not in our namespace
205
+ myns="$(readlink /proc/self/ns/pid 2> /dev/null)"
206
+ for p in ${pids}; do
207
+ ns="$(readlink "/proc/${p}/ns/pid" 2> /dev/null)"
208
+
209
+ # if pid of systemd is in our namespace, it is systemd
210
+ [ -n "${myns}" ] && [ "${myns}" = "${ns}" ] && return 0
211
+ done
212
+
213
+ # else, it is not systemd
214
+ return 1
215
+}
216
+
217
+check_systemd() {
218
+ if [ -z "${IS_SYSTEMD}" ]; then
219
+ issystemd
220
+ IS_SYSTEMD="$?"
221
+ fi
222
+
223
+ return "${IS_SYSTEMD}"
224
+}
225
+
226
+get_systemd_service_dir() {
227
+ if [ -w "/lib/systemd/system" ]; then
228
+ echo "/lib/systemd/system"
229
+ elif [ -w "/usr/lib/systemd/system" ]; then
230
+ echo "/usr/lib/systemd/system"
231
+ elif [ -w "/etc/systemd/system" ]; then
232
+ echo "/etc/systemd/system"
233
+ else
234
+ error "Unable to detect systemd service directory."
235
+ exit 4
236
+ fi
237
+}
238
+
239
+install_systemd_service() {
240
+ SRCFILE="${SVC_SOURCE}/netdata.service"
241
+
242
+ if [ "$(systemctl --version | head -n 1 | cut -f 2 -d ' ')" -le 235 ]; then
243
+ SRCFILE="${SVC_SOURCE}/netdata.service.v235"
244
+ fi
245
+
246
+ if [ "${ENABLE}" = "auto" ]; then
247
+ IS_NETDATA_ENABLED="$(systemctl is-enabled netdata 2> /dev/null || echo "Netdata not there")"
248
+
249
+ if [ "${IS_NETDATA_ENABLED}" = "disabled" ]; then
250
+ ENABLE="disable"
251
+ else
252
+ ENABLE="enable"
253
+ fi
254
+ fi
255
+
256
+ info "Installing systemd service..."
257
+ if ! install -p -m 0644 -o 0 -g 0 "${SRCFILE}" "$(get_systemd_service_dir)/netdata.service"; then
258
+ error "Failed to install systemd service file."
259
+ exit 4
260
+ fi
261
+
262
+ if ! systemctl daemon-reload; then
263
+ warning "Failed to reload systemd unit files."
264
+ fi
265
+
266
+ if ! systemctl ${ENABLE} netdata; then
267
+ warning "Failed to ${ENABLE} Netdata service."
268
+ fi
269
+}
270
+
271
+systemd_cmds() {
272
+ NETDATA_START_CMD='systemctl start netdata'
273
+ NETDATA_STOP_CMD='systemctl stop netdata'
274
+}
275
+
276
+# =====================================================================
277
+# OpenRC support functions
278
+
279
+isopenrc() {
280
+ # if /lib/rc/sh/functions.sh does not exist, it's not OpenRC
281
+ [ ! -f /lib/rc/sh/functions.sh ] && return 1
282
+
283
+ # if there is no /etc/init.d, it's not OpenRC
284
+ [ ! -d /etc/init.d ] && return 1
285
+
286
+ # if PID 1 is openrc-init, it's OpenRC
287
+ [ "$(basename "$(readlink /proc/1/exe)" 2> /dev/null)" = "openrc-init" ] && return 0
288
+
289
+ # If /run/openrc/softlevel exists, it's OpenRC
290
+ [ -f /run/openrc/softlevel ] && return 0
291
+
292
+ # if there is an openrc command, it's OpenRC
293
+ command -v openrc > /dev/null 2>&1 && return 0
294
+
295
+ # Otherwise, it’s not OpenRC
296
+ return 1
297
+}
298
+
299
+check_openrc() {
300
+ if [ -z "${IS_OPENRC}" ]; then
301
+ isopenrc
302
+ IS_OPENRC="$?"
303
+ fi
304
+
305
+ return "${IS_OPENRC}"
306
+}
307
+
308
+enable_openrc() {
309
+ runlevel="$(rc-status -r)"
310
+ runlevel="${runlevel:-default}"
311
+ if ! rc-update add netdata "${runlevel}"; then
312
+ warning "Failed to enable Netdata service in runlevel ${runlevel}."
313
+ fi
314
+}
315
+
316
+disable_openrc() {
317
+ for runlevel in /etc/runlevels/*; do
318
+ if [ -e "${runlevel}/netdata" ]; then
319
+ runlevel="$(basename "${runlevel}")"
320
+ if ! rc-update del netdata "${runlevel}"; then
321
+ warning "Failed to disable Netdata service in runlevel ${runlevel}."
322
+ fi
323
+ fi
324
+ done
325
+}
326
+
327
+install_openrc_service() {
328
+ install_generic_service openrc OpenRC /etc/init.d/netdata enable_openrc disable_openrc
329
+}
330
+
331
+openrc_cmds() {
332
+ NETDATA_START_CMD='rc-service netdata start'
333
+ NETDATA_STOP_CMD='rc-service netdata stop'
334
+}
335
+
336
+# =====================================================================
337
+# LSB init script support functions
338
+
339
+islsb() {
340
+ # if there is no /etc/init.d directory, it’s not an LSB system
341
+ [ ! -d /etc/init.d ] && return 1
342
+
343
+ # If it's an OpenRC system, then it's not an LSB system
344
+ check_openrc && return 1
345
+
346
+ # If /lib/lsb/init-functions exists, it’s an LSB system
347
+ [ -f /lib/lsb/init-functions ] && return 0
348
+
349
+ return 1
350
+}
351
+
352
+check_lsb() {
353
+ if [ -z "${IS_LSB}" ]; then
354
+ islsb
355
+ IS_LSB="$?"
356
+ fi
357
+
358
+ return "${IS_LSB}"
359
+}
360
+
361
+enable_lsb() {
362
+ if ! update-rc.d netdata defaults; then
363
+ warning "Failed to enable Netdata service."
364
+ elif ! update-rc.d netdata defaults-disable; then
365
+ warning "Failed to fully enable Netdata service."
366
+ fi
367
+}
368
+
369
+disable_lsb() {
370
+ if ! update-rc.d netdata remove; then
371
+ warning "Failed to disable Netdata service."
372
+ fi
373
+}
374
+
375
+install_lsb_service() {
376
+ install_generic_service lsb LSB /etc/init.d/netdata enable_lsb disable_lsb
377
+}
378
+
379
+lsb_cmds() {
380
+ if command -v service >/dev/null 2>&1; then
381
+ NETDATA_START_CMD='service netdata start'
382
+ NETDATA_STOP_CMD='service netdata stop'
383
+ else
384
+ NETDATA_START_CMD='/etc/init.d/netdata start'
385
+ NETDATA_STOP_CMD='/etc/init.d/netdata stop'
386
+ fi
387
+}
388
+
389
+# =====================================================================
390
+# init.d init script support functions
391
+
392
+isinitd() {
393
+ # if there is no /etc/init.d directory, it’s not an init.d system
394
+ [ ! -d /etc/init.d ] && return 1
395
+
396
+ # if there is no chkconfig command, it's not a (usable) init.d system
397
+ command -v chkconfig >/dev/null 2>&1 || return 1
398
+
399
+ # if it's not an LSB setup, it’s init.d
400
+ check_initd || return 0
401
+
402
+ return 1
403
+}
404
+
405
+check_initd() {
406
+ if [ -z "${IS_INITD}" ]; then
407
+ isinitd
408
+ IS_INITD="$?"
409
+ fi
410
+
411
+ return "${IS_INITD}"
412
+}
413
+
414
+enable_initd() {
415
+ if ! chkconfig netdata on; then
416
+ warning "Failed to enable Netdata service."
417
+ fi
418
+}
419
+
420
+disable_initd() {
421
+ if ! chkconfig netdata off; then
422
+ warning "Failed to disable Netdata service."
423
+ fi
424
+}
425
+
426
+install_initd_service() {
427
+ install_generic_service init-d init.d /etc/init.d/netdata enable_initd disable_initd
428
+}
429
+
430
+initd_cmds() {
431
+ if command -v service >/dev/null 2>&1; then
432
+ NETDATA_START_CMD='service netdata start'
433
+ NETDATA_STOP_CMD='service netdata stop'
434
+ else
435
+ NETDATA_START_CMD='/etc/init.d/netdata start'
436
+ NETDATA_STOP_CMD='/etc/init.d/netdata stop'
437
+ fi
438
+}
439
+
440
+# =====================================================================
441
+# runit support functions
442
+#
443
+# Currently not supported, this exists to provide useful error messages.
444
+
445
+isrunit() {
446
+ # if there is no /lib/rc/sv.d, then it's not runit
447
+ [ ! -d /lib/rc/sv.d ] && return 1
448
+
449
+ # if there is no runit command, then it's not runit
450
+ command -v runit >/dev/null 2>&1 || return 1
451
+
452
+ # if /run/runit exists, then it's runit
453
+ [ -d /run/runit ] && return 0
454
+
455
+ # if /etc/runit/1 exists and is executable, then it's runit
456
+ [ -x /etc/runit/1 ] && return 0
457
+
458
+ return 1
459
+}
460
+
461
+check_runit() {
462
+ if [ -z "${IS_RUNIT}" ]; then
463
+ isrunit
464
+ IS_RUNIT="$?"
465
+ fi
466
+
467
+ return "${IS_RUNIT}"
468
+}
469
+
470
+install_runit_service() {
471
+ error "Detected runit, which we do not currently support."
472
+ exit 3
473
+}
474
+
475
+runit_cmds() {
476
+ error "Detected runit, which we do not currently support."
477
+ exit 3
478
+}
479
+
480
+# =====================================================================
481
+# WSL support functions
482
+#
483
+# Cannot be supported, this exists to provide useful error messages.
484
+
485
+iswsl() {
486
+ # If uname -r contains the string WSL, then it's WSL.
487
+ uname -r | grep -q 'WSL' && return 0
488
+
489
+ # If uname -r contains the string Microsoft, then it's WSL.
490
+ # This probably throws a false positive on CBL-Mariner, but it's part of what MS officially recommends for
491
+ # detecting if you're running under WSL.
492
+ uname -r | grep -q "Microsoft" && return 0
493
+
494
+ return 1
495
+}
496
+
497
+check_wsl() {
498
+ if [ -z "${IS_WSL}" ]; then
499
+ iswsl
500
+ IS_WSL="$?"
501
+ fi
502
+
503
+ return "${IS_WSL}"
504
+}
505
+
506
+install_wsl_service() {
507
+ error "We appear to be running in WSL. Netdata cannot be automatically installed as a service under WSL."
508
+ exit 3
509
+}
510
+
511
+wsl_cmds() {
512
+ error "We appear to be running in WSL. Netdata cannot be automatically installed as a service under WSL."
513
+ exit 3
514
+}
515
+
516
+# =====================================================================
517
+# FreeBSD support functions
518
+
519
+enable_freebsd() {
520
+ if ! sysrc netdata_enable=YES; then
521
+ warning "Failed to enable netdata service."
522
+ fi
523
+}
524
+
525
+disable_freebsd() {
526
+ if ! sysrc netdata_enable=NO; then
527
+ warning "Failed to disable netdata service."
528
+ fi
529
+}
530
+
531
+install_freebsd_service() {
532
+ install_generic_service freebsd "FreeBSD rc.d" /usr/local/etc/rc.d/netdata enable_freebsd disable_freebsd
533
+}
534
+
535
+freebsd_cmds() {
536
+ NETDATA_START_CMD='service netdata start'
537
+ NETDATA_STOP_CMD='service netdata stop'
538
+ NETDATA_INSTALLER_START_CMD='service netdata onestart'
539
+}
540
+
541
+# =====================================================================
542
+# macOS support functions
543
+
544
+install_darwin_service() {
545
+ info "Installing macOS plist file for launchd."
546
+ if ! install -C -S -p -m 0644 -o 0 -g 0 system/netdata.plist /Library/LaunchDaemons/com.github.netdata.plist; then
547
+ error "Failed to copy plist file."
548
+ exit 4
549
+ fi
550
+
551
+ if ! launchctl load /Library/LaunchDaemons/com.github.netdata.plist; then
552
+ error "Failed to load plist file."
553
+ exit 4
554
+ fi
555
+}
556
+
557
+darwin_cmds() {
558
+ NETDATA_START_CMD='launchctl start com.github.netdata'
559
+ NETDATA_STOP_CMD='launchctl stop com.github.netdata'
560
+}
561
+
562
+# =====================================================================
563
+# Linux support functions
564
+
565
+detect_linux_svc_type() {
566
+ if [ "${SVC_TYPE}" = "detect" ]; then
567
+ for t in ${LINUX_INIT_TYPES}; do
568
+ if "check_${t}"; then
569
+ SVC_TYPE="${t}"
570
+ break
571
+ fi
572
+ done
573
+
574
+ if [ "${SVC_TYPE}" = "detect" ]; then
575
+ error "Failed to detect what type of service manager is in use."
576
+ else
577
+ echo "${SVC_TYPE}"
578
+ fi
579
+ else
580
+ echo "${SVC_TYPE}"
581
+ fi
582
+}
583
+
584
+install_linux_service() {
585
+ t="$(detect_linux_svc_type)"
586
+
587
+ if [ -z "${t}" ]; then
588
+ exit 2
589
+ fi
590
+
591
+ "install_${t}_service"
592
+}
593
+
594
+linux_cmds() {
595
+ t="$(detect_linux_svc_type)"
596
+
597
+ if [ -z "${t}" ]; then
598
+ exit 2
599
+ fi
600
+
601
+ "${t}_cmds"
602
+}
603
+
604
+# =====================================================================
605
+# Argument handling
606
+
607
+parse_args() {
608
+ while [ -n "${1}" ]; do
609
+ case "${1}" in
610
+ "--source" | "-s")
611
+ SVC_SOURCE="${2}"
612
+ shift 1
613
+ ;;
614
+ "--type" | "-t")
615
+ if [ "${2}" = "help" ]; then
616
+ help_types
617
+ exit 0
618
+ else
619
+ SVC_TYPE="${2}"
620
+ shift 1
621
+ fi
622
+ ;;
623
+ "--save-cmds")
624
+ if [ -z "${2}" ]; then
625
+ info "No path specified to save command variables."
626
+ exit 1
627
+ else
628
+ SAVE_CMDS_PATH="${2}"
629
+ shift 1
630
+ fi
631
+ ;;
632
+ "--cmds" | "-c") DUMP_CMDS=1 ;;
633
+ "--cmds-only") INSTALL=0 ;;
634
+ "--export-cmds") EXPORT_CMDS=1 ;;
635
+ "--enable" | "-e") ENABLE="enable" ;;
636
+ "--disable" | "-d") ENABLE="disable" ;;
637
+ "--help" | "-h")
638
+ usage
639
+ exit 0
640
+ ;;
641
+ *)
642
+ info "Unrecognized option '${1}'."
643
+ exit 1
644
+ ;;
645
+ esac
646
+ shift 1
647
+ done
648
+
649
+ if [ "${SVC_SOURCE#@}" = "libsysdir_POST@" ]; then
650
+ SVC_SOURCE="$(dirname "${SCRIPT_SOURCE}")/../../lib/netdata/system"
651
+ warning "SVC_SOURCE not templated, using ${SVC_SOURCE} as source directory."
652
+ fi
653
+
654
+ if [ ! -d "${SVC_SOURCE}" ] && [ "${INSTALL}" -eq 1 ]; then
655
+ error "${SVC_SOURCE} does not appear to be a directory. Please specify a valid source for service files with the --source option."
656
+ exit 1
657
+ fi
658
+
659
+ if valid_types | grep -vw "${SVC_TYPE}"; then
660
+ error "${SVC_TYPE} is not supported on this platform."
661
+ help_types
662
+ exit 1
663
+ fi
664
+}
665
+
666
+# =====================================================================
667
+# Core logic
668
+
669
+main() {
670
+ trap "cleanup" EXIT
671
+
672
+ parse_args "${@}"
673
+
674
+ case "${PLATFORM}" in
675
+ FreeBSD)
676
+ [ "${INSTALL}" -eq 1 ] && install_freebsd_service
677
+ freebsd_cmds
678
+ ;;
679
+ Darwin)
680
+ [ "${INSTALL}" -eq 1 ] && install_darwin_service
681
+ darwin_cmds
682
+ ;;
683
+ Linux)
684
+ [ "${INSTALL}" -eq 1 ] && install_linux_service
685
+ linux_cmds
686
+ ;;
687
+ *)
688
+ error "${PLATFORM} is not supported by this script."
689
+ exit 5
690
+ ;;
691
+ esac
692
+
693
+ [ "${DUMP_CMDS}" -eq 1 ] && dump_cmds
694
+ [ "${EXPORT_CMDS}" -eq 1 ] && export_cmds
695
+ [ -n "${SAVE_CMDS_PATH}" ] && save_cmds
696
+ exit 0
697
+}
698
+
699
+main "${@}"