@cryptotaxi247 / netdata-1 / commits / f4a4918cc

Further improvements to the new service installation code. (#13774)

* Improve OpenRC detection. - Check for rc-update command, without that we don’t work so it’s ‘not OpenRC’. - Sort checks so that less expensive ones run first. - If we see `/etc/init.d/local` and it’s got a shebang line with `openrc-run`, then assume we’re dealing with OpenRC. This file gets installed by default as part of OpenRC itself. * Fix runit detection. - `/lib/rc/sv.d` is only used on some runit systems (notably Artix), not all, so don’t bail if we can’t find it. - Check not just for a `runit` command, but also `runsvdir`. * Add support for systemd in WSL. THis just involves running the normal systemd check if we detected WSL, and updating the service manager type appropriately. * Fix tmpdir handling for service installation. * Fix invocation of service install script. * Add fallback to detected but not running service managers. Currently supported for systemd and OpenRC. If no other service manager is detected, but we detected that either systemd or OpenRC is present but not running, fall back to them instead of outright failing. This does not allow starting/stopping the agent using the service manager, but _does_ happen to work for installing the service properly on an offline system. * Fix service-type fallback handling. If we see systemd, even if it not booted into it, we should not install init.d or LSB init scripts. * Fix recursion bug in initd install type checking. * Skip initd if OpenRC is found. Just like systemd, any OpenRC on the system automatically means that it’s _not_ a classic init.d setup. * Fall back to init.d in WSL in some cases. If there is no _usable_ systemd, and the service manager otherwise looks like an init.d setup, fall back to an init.d setup. * Support LSB style init scripts in WSL as well. Also consolidate and update the error message that gets printed when running under WSL. * Fix typo in LSB init script handling code. * Fix command handling for LSB and initd service management. Instead of using `service`, just run the init scripts directly. This way we avoid any bugs in `service` that would cause it to pretend that systemd is in use even though it isn’t. * Fix botched partial commit. * Restructure service type detection to not rely on return codes. * Added option to display service type detection results. This should simplify debugging.

Austin S. Hemmelgarn committed Oct 17, 2022 at 08:25 UTC f4a4918cc9aef526cb02eec1e3629c148c2af06b
2 files changed +205 -120
packaging/installer/functions.sh
+6 -2
@@ -476,10 +476,14 @@ install_non_systemd_init() {
476 }
477
478 run_install_service_script() {
479 + if [ -z "${tmpdir}" ]; then
480 + tmpdir="${TMPDIR:-/tmp}"
481 + fi
482 +
483 # shellcheck disable=SC2154
484 save_path="${tmpdir}/netdata-service-cmds"
485 # shellcheck disable=SC2068
482 - run "${NETDATA_PREFIX}/usr/libexec/netdata/install-service.sh" --save-cmds "${save_path}" ${@}
486 + "${NETDATA_PREFIX}/usr/libexec/netdata/install-service.sh" --save-cmds "${save_path}" ${@}
487
488 case $? in
489 0)
@@ -532,7 +536,7 @@ run_install_service_script() {
536 install_netdata_service() {
537 if [ "${UID}" -eq 0 ]; then
538 if [ -x "${NETDATA_PREFIX}/usr/libexec/netdata/install-service.sh" ]; then
535 - run_install_service_script
539 + run_install_service_script && return 0
540 else
541 # This is used by netdata-installer.sh
542 # shellcheck disable=SC2034
system/install-service.sh.in
+199 -118
@@ -29,10 +29,12 @@ DUMP_CMDS=0
29 ENABLE="auto"
30 EXPORT_CMDS=0
31 INSTALL=1
32 -LINUX_INIT_TYPES="wsl systemd openrc lsb initd runit"
32 +LINUX_INIT_TYPES="systemd openrc lsb initd runit"
33 PLATFORM="$(uname -s)"
34 +SHOW_SVC_TYPE=0
35 SVC_SOURCE="@libsysdir_POST@"
36 SVC_TYPE="detect"
37 +WSL_ERROR_MSG="We appear to be running in WSL and were unable to find a usable service manager. We currently support systemd, LSB init scripts, and traditional init.d style init scripts when running under WSL."
38
39 # =====================================================================
40 # Utility functions
@@ -157,6 +159,7 @@ USAGE: install-service.sh [options]
159
160 --source Specify where to find the service files to install (default ${SVC_SOURCE}).
161 --type Specify the type of service file to install. Specify a type of 'help' to get a list of valid types for your platform.
162 + --show-type Display information about what service managers are detected.
163 --cmds Additionally print a list of commands for starting and stopping the agent with the detected service type.
164 --export-cmds Export the variables that would be printed by the --cmds option.
165 --cmds-only Don't install, just handle the --cmds or --export-cmds option.
@@ -176,30 +179,27 @@ HEREDOC
179 # =====================================================================
180 # systemd support functions
181
179 -issystemd() {
182 +_check_systemd() {
183 pids=''
184 p=''
185 myns=''
186 ns=''
184 - systemctl=''
187
188 # if the directory /lib/systemd/system OR /usr/lib/systemd/system (SLES 12.x) does not exit, it is not systemd
189 if [ ! -d /lib/systemd/system ] && [ ! -d /usr/lib/systemd/system ]; then
188 - return 1
190 + echo "NO" && return 0
191 fi
192
193 # 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
194 + [ -z "$(command -v systemctl 2>/dev/null || true)" ] && echo "NO" && return 0
195
196 # if pid 1 is systemd, it is systemd
198 - [ "$(basename "$(readlink /proc/1/exe)" 2> /dev/null)" = "systemd" ] && return 0
197 + [ "$(basename "$(readlink /proc/1/exe)" 2> /dev/null)" = "systemd" ] && echo "YES" && return 0
198
200 - # if systemd is not running, it is not systemd
199 + # it ‘is’ systemd at this point, but systemd might not be running
200 + # if not, return 2 to indicate ‘systemd, but not running’
201 pids=$(safe_pidof systemd 2> /dev/null)
202 - [ -z "${pids}" ] && return 1
202 + [ -z "${pids}" ] && echo "OFFLINE" && return 0
203
204 # check if the running systemd processes are not in our namespace
205 myns="$(readlink /proc/self/ns/pid 2> /dev/null)"
@@ -207,20 +207,19 @@ issystemd() {
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
210 + [ -n "${myns}" ] && [ "${myns}" = "${ns}" ] && echo "YES" && return 0
211 done
212
213 # else, it is not systemd
214 - return 1
214 + echo "NO"
215 }
216
217 check_systemd() {
218 if [ -z "${IS_SYSTEMD}" ]; then
219 - issystemd
220 - IS_SYSTEMD="$?"
219 + IS_SYSTEMD="$(_check_systemd)"
220 fi
221
223 - return "${IS_SYSTEMD}"
222 + echo "${IS_SYSTEMD}"
223 }
224
225 get_systemd_service_dir() {
@@ -259,50 +258,61 @@ install_systemd_service() {
258 exit 4
259 fi
260
262 - if ! systemctl daemon-reload; then
263 - warning "Failed to reload systemd unit files."
264 - fi
261 + if check_systemd; then
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."
266 + if ! systemctl ${ENABLE} netdata; then
267 + warning "Failed to ${ENABLE} Netdata service."
268 + fi
269 fi
270 }
271
272 systemd_cmds() {
272 - NETDATA_START_CMD='systemctl start netdata'
273 - NETDATA_STOP_CMD='systemctl stop netdata'
273 + if check_systemd; then
274 + NETDATA_START_CMD='systemctl start netdata'
275 + NETDATA_STOP_CMD='systemctl stop netdata'
276 + else # systemd is not running, use external defaults by providing no commands
277 + warning "Detected systemd, but not booted using systemd. Unable to provide commands to start or stop Netdata using the service manager."
278 + fi
279 }
280
281 # =====================================================================
282 # OpenRC support functions
283
279 -isopenrc() {
284 +_check_openrc() {
285 # if /lib/rc/sh/functions.sh does not exist, it's not OpenRC
281 - [ ! -f /lib/rc/sh/functions.sh ] && return 1
286 + [ ! -f /lib/rc/sh/functions.sh ] && echo "NO" && return 0
287
288 # if there is no /etc/init.d, it's not OpenRC
284 - [ ! -d /etc/init.d ] && return 1
289 + [ ! -d /etc/init.d ] && echo "NO" && return 0
290
286 - # if PID 1 is openrc-init, it's OpenRC
287 - [ "$(basename "$(readlink /proc/1/exe)" 2> /dev/null)" = "openrc-init" ] && return 0
291 + # if there is no rc-update command, it's not OpenRC
292 + [ -z "$(command -v rc-update 2>/dev/null || true)" ] && echo "NO" && return 0
293
294 # If /run/openrc/softlevel exists, it's OpenRC
290 - [ -f /run/openrc/softlevel ] && return 0
295 + [ -f /run/openrc/softlevel ] && echo "YES" && return 0
296 +
297 + # if PID 1 is openrc-init, it's OpenRC
298 + [ "$(basename "$(readlink /proc/1/exe)" 2> /dev/null)" = "openrc-init" ] && echo "YES" && return 0
299
292 - # if there is an openrc command, it's OpenRC
293 - command -v openrc > /dev/null 2>&1 && return 0
300 + # if there is an openrc command, it's OpenRC, but not booted as such
301 + [ -n "$(command -v openrc 2>/dev/null || true)" ] && echo "OFFLINE" && return 0
302 +
303 + # if /etc/init.d/local exists and has `openrc-run` in it's shebang line, it’s OpenRC, but not booted as such
304 + [ -r /etc/init.d/local ] && head -n 1 /etc/init.d/local | grep -q openrc-run && echo "OFFLINE" && return 0
305
306 # Otherwise, it’s not OpenRC
296 - return 1
307 + echo "NO" && return 0
308 }
309
310 check_openrc() {
311 if [ -z "${IS_OPENRC}" ]; then
301 - isopenrc
302 - IS_OPENRC="$?"
312 + IS_OPENRC="$(_check_openrc)"
313 fi
314
305 - return "${IS_OPENRC}"
315 + echo "${IS_OPENRC}"
316 }
317
318 enable_openrc() {
@@ -329,39 +339,49 @@ install_openrc_service() {
339 }
340
341 openrc_cmds() {
332 - NETDATA_START_CMD='rc-service netdata start'
333 - NETDATA_STOP_CMD='rc-service netdata stop'
342 + if check_openrc; then
343 + NETDATA_START_CMD='rc-service netdata start'
344 + NETDATA_STOP_CMD='rc-service netdata stop'
345 + else # Not booted using OpenRC, use external defaults by not providing commands.
346 + warning "Detected OpenRC, but the system is not booted using OpenRC. Unable to provide commands to start or stop Netdata using the service manager."
347 + fi
348 }
349
350 # =====================================================================
351 # LSB init script support functions
352
339 -islsb() {
353 +_check_lsb_ignore_systemd() {
354 # if there is no /etc/init.d directory, it’s not an LSB system
341 - [ ! -d /etc/init.d ] && return 1
355 + [ ! -d /etc/init.d ] && echo "NO" && return 0
356
357 # If it's an OpenRC system, then it's not an LSB system
344 - check_openrc && return 1
358 + [ "$(check_openrc)" != "NO" ] && echo "NO" && return 0
359
360 # If /lib/lsb/init-functions exists, it’s an LSB system
347 - [ -f /lib/lsb/init-functions ] && return 0
361 + [ -f /lib/lsb/init-functions ] && echo "YES" && return 0
362 +
363 + echo "NO" && return 0
364 +}
365
349 - return 1
366 +_check_lsb() {
367 + # if there is _any_ systemd, it’s not an LSB system
368 + [ "$(check_systemd)" != "NO" ] && echo "NO" && return 0
369 +
370 + _check_lsb_ignore_systemd
371 }
372
373 check_lsb() {
374 if [ -z "${IS_LSB}" ]; then
354 - islsb
355 - IS_LSB="$?"
375 + IS_LSB="$(_check_lsb)"
376 fi
377
358 - return "${IS_LSB}"
378 + echo "${IS_LSB}"
379 }
380
381 enable_lsb() {
382 if ! update-rc.d netdata defaults; then
383 warning "Failed to enable Netdata service."
364 - elif ! update-rc.d netdata defaults-disable; then
384 + elif ! update-rc.d netdata defaults-disabled; then
385 warning "Failed to fully enable Netdata service."
386 fi
387 }
@@ -377,38 +397,42 @@ install_lsb_service() {
397 }
398
399 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
400 + NETDATA_START_CMD='/etc/init.d/netdata start'
401 + NETDATA_STOP_CMD='/etc/init.d/netdata stop'
402 }
403
404 # =====================================================================
405 # init.d init script support functions
406
392 -isinitd() {
407 +_check_initd_ignore_systemd() {
408 # if there is no /etc/init.d directory, it’s not an init.d system
394 - [ ! -d /etc/init.d ] && return 1
409 + [ ! -d /etc/init.d ] && echo "NO" && return 1
410
411 # if there is no chkconfig command, it's not a (usable) init.d system
397 - command -v chkconfig >/dev/null 2>&1 || return 1
412 + [ -z "$(command -v chkconfig 2>/dev/null || true)" ] && echo "NO" && return 0
413 +
414 + # if there is _any_ openrc, it’s not init.d
415 + [ "$(check_openrc)" != "NO" ] && echo "NO" && return 0
416
417 # if it's not an LSB setup, it’s init.d
400 - check_initd || return 0
418 + [ "$(check_lsb)" != "NO" ] && echo "NO" && return 0
419
402 - return 1
420 + echo "YES" && return 0
421 +}
422 +
423 +_check_initd() {
424 + # if there is _any_ systemd, it’s not init.d
425 + [ "$(check_systemd)" != "NO" ] && echo "NO" && return 0
426 +
427 + _check_initd_ignore_systemd
428 }
429
430 check_initd() {
431 if [ -z "${IS_INITD}" ]; then
407 - isinitd
408 - IS_INITD="$?"
432 + IS_INITD="$(_check_initd)"
433 fi
434
411 - return "${IS_INITD}"
435 + echo "${IS_INITD}"
436 }
437
438 enable_initd() {
@@ -428,13 +452,8 @@ install_initd_service() {
452 }
453
454 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
455 + NETDATA_START_CMD='/etc/init.d/netdata start'
456 + NETDATA_STOP_CMD='/etc/init.d/netdata stop'
457 }
458
459 # =====================================================================
@@ -442,29 +461,28 @@ initd_cmds() {
461 #
462 # Currently not supported, this exists to provide useful error messages.
463
445 -isrunit() {
446 - # if there is no /lib/rc/sv.d, then it's not runit
447 - [ ! -d /lib/rc/sv.d ] && return 1
464 +_check_runit() {
465 + # if there is no runsvdir command, then it's not runit
466 + [ -z "$(command -v runsvdir 2>/dev/null || true)" ] && echo "NO" && return 0
467
468 # if there is no runit command, then it's not runit
450 - command -v runit >/dev/null 2>&1 || return 1
469 + [ -z "$(command -v runit 2>/dev/null || true)" ] && echo "NO" && return 0
470
471 # if /run/runit exists, then it's runit
453 - [ -d /run/runit ] && return 0
472 + [ -d /run/runit ] && echo "YES" && return 0
473
474 # if /etc/runit/1 exists and is executable, then it's runit
456 - [ -x /etc/runit/1 ] && return 0
475 + [ -x /etc/runit/1 ] && echo "YES" && return 0
476
458 - return 1
477 + echo "NO" && return 0
478 }
479
480 check_runit() {
481 if [ -z "${IS_RUNIT}" ]; then
463 - isrunit
464 - IS_RUNIT="$?"
482 + IS_RUNIT="$(_check_runit)"
483 fi
484
467 - return "${IS_RUNIT}"
485 + echo "${IS_RUNIT}"
486 }
487
488 install_runit_service() {
@@ -482,34 +500,33 @@ runit_cmds() {
500 #
501 # Cannot be supported, this exists to provide useful error messages.
502
485 -iswsl() {
503 +_check_wsl() {
504 # If uname -r contains the string WSL, then it's WSL.
487 - uname -r | grep -q 'WSL' && return 0
505 + uname -r | grep -q 'WSL' && echo "YES" && return 0
506
507 # If uname -r contains the string Microsoft, then it's WSL.
508 # This probably throws a false positive on CBL-Mariner, but it's part of what MS officially recommends for
509 # detecting if you're running under WSL.
492 - uname -r | grep -q "Microsoft" && return 0
510 + uname -r | grep -q "Microsoft" && echo "YES" && return 0
511
494 - return 1
512 + echo "NO" && return 0
513 }
514
515 check_wsl() {
516 if [ -z "${IS_WSL}" ]; then
499 - iswsl
500 - IS_WSL="$?"
517 + IS_WSL="$(_check_wsl)"
518 fi
519
503 - return "${IS_WSL}"
520 + echo "${IS_WSL}"
521 }
522
523 install_wsl_service() {
507 - error "We appear to be running in WSL. Netdata cannot be automatically installed as a service under WSL."
524 + error "${WSL_ERROR_MSG}"
525 exit 3
526 }
527
528 wsl_cmds() {
512 - error "We appear to be running in WSL. Netdata cannot be automatically installed as a service under WSL."
529 + error "${WSL_ERROR_MSG}"
530 exit 3
531 }
532
@@ -564,21 +581,47 @@ darwin_cmds() {
581
582 detect_linux_svc_type() {
583 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
584 + found_types=''
585 +
586 + for t in wsl ${LINUX_INIT_TYPES}; do
587 + case "$("check_${t}")" in
588 + YES)
589 + SVC_TYPE="${t}"
590 + break
591 + ;;
592 + NO) continue ;;
593 + OFFLINE)
594 + if [ -z "${found_types}" ]; then
595 + found_types="${t}"
596 + else
597 + found_types="${found_types} ${t}"
598 + fi
599 + ;;
600 + esac
601 done
602
603 if [ "${SVC_TYPE}" = "detect" ]; then
575 - error "Failed to detect what type of service manager is in use."
576 - else
577 - echo "${SVC_TYPE}"
604 + if [ -z "${found_types}" ]; then
605 + error "Failed to detect what type of service manager is in use."
606 + else
607 + SVC_TYPE="$(echo "${found_types}" | cut -f 1 -d ' ')"
608 + warning "Failed to detect a running service manager, using detected (but not running) ${SVC_TYPE}."
609 + fi
610 + elif [ "${SVC_TYPE}" = "wsl" ]; then
611 + if [ "$(check_systemd)" = "YES" ]; then
612 + # Support for systemd in WSL.
613 + SVC_TYPE="systemd"
614 + elif [ "$(_check_lsb_ignore_systemd)" = "YES" ]; then
615 + # Support for LSB init.d in WSL.
616 + SVC_TYPE="lsb"
617 + elif [ "$(_check_initd_ignore_systemd)" = "YES" ]; then
618 + # Support for ‘generic’ init.d in WSL.
619 + SVC_TYPE="initd"
620 + fi
621 fi
579 - else
580 - echo "${SVC_TYPE}"
622 fi
623 +
624 + echo "${SVC_TYPE}"
625 }
626
627 install_linux_service() {
@@ -601,6 +644,38 @@ linux_cmds() {
644 "${t}_cmds"
645 }
646
647 +# =====================================================================
648 +# Service type display function
649 +
650 +show_service_type() {
651 + info "Detected platform: ${PLATFORM}"
652 +
653 + case "${PLATFORM}" in
654 + FreeBSD)
655 + info "Detected service managers:"
656 + info " - freebsd: YES"
657 + info "Would use freebsd service management."
658 + ;;
659 + Darwin)
660 + info "Detected service managers:"
661 + info " - launchd: YES"
662 + info "Would use launchd service management."
663 + ;;
664 + Linux)
665 + [ "$(check_wsl)" = "YES" ] && info "Detected WSL environment."
666 + info "Detected service managers:"
667 + for t in ${LINUX_INIT_TYPES}; do
668 + info " - ${t}: $("check_${t}")"
669 + done
670 + info "Would use $(detect_linux_svc_type) service management."
671 + ;;
672 + *)
673 + info "${PLATFORM} is not supported by this script. No service file would be installed."
674 + esac
675 +
676 + exit 0
677 +}
678 +
679 # =====================================================================
680 # Argument handling
681
@@ -620,6 +695,7 @@ parse_args() {
695 shift 1
696 fi
697 ;;
698 + "--show-type") SHOW_SVC_TYPE=1 ; INSTALL=0 ;;
699 "--save-cmds")
700 if [ -z "${2}" ]; then
701 info "No path specified to save command variables."
@@ -671,28 +747,33 @@ main() {
747
748 parse_args "${@}"
749
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
750 + if [ "${SHOW_SVC_TYPE}" -eq 1 ]; then
751 + show_service_type
752 + else
753 + case "${PLATFORM}" in
754 + FreeBSD)
755 + [ "${INSTALL}" -eq 1 ] && install_freebsd_service
756 + freebsd_cmds
757 + ;;
758 + Darwin)
759 + [ "${INSTALL}" -eq 1 ] && install_darwin_service
760 + darwin_cmds
761 + ;;
762 + Linux)
763 + [ "${INSTALL}" -eq 1 ] && install_linux_service
764 + linux_cmds
765 + ;;
766 + *)
767 + error "${PLATFORM} is not supported by this script."
768 + exit 5
769 + ;;
770 + esac
771 +
772 + [ "${DUMP_CMDS}" -eq 1 ] && dump_cmds
773 + [ "${EXPORT_CMDS}" -eq 1 ] && export_cmds
774 + [ -n "${SAVE_CMDS_PATH}" ] && save_cmds
775 + fi
776
693 - [ "${DUMP_CMDS}" -eq 1 ] && dump_cmds
694 - [ "${EXPORT_CMDS}" -eq 1 ] && export_cmds
695 - [ -n "${SAVE_CMDS_PATH}" ] && save_cmds
777 exit 0
778 }
779