#!/bin/bash

## Copyright (C) 2012 - 2025 ENCRYPTED SUPPORT LLC <adrelanos@whonix.org>
## See the file COPYING for copying conditions.

check_services_do() {
   ## user@host:~$ sudo systemctl --failed --no-legend list-units
   ## apparmor.service        loaded failed failed LSB: AppArmor initialization
   ## whonix-firewall.service loaded failed failed Whonix firewall loader
   ##
   ## user@host:~$ sudo systemctl --failed list-units
   ##   UNIT                    LOAD   ACTIVE SUB    DESCRIPTION
   ## * apparmor.service        loaded failed failed LSB: AppArmor initialization
   ## * whonix-firewall.service loaded failed failed Whonix firewall loader
   ##
   ## LOAD   = Reflects whether the unit definition was properly loaded.
   ## ACTIVE = The high-level unit activation state, i.e. generalization of SUB.
   ## SUB    = The low-level unit activation state, values depend on unit type.
   ##
   ## 2 loaded units listed. Pass --all to see loaded but inactive units, too.
   ## To show all installed unit files use 'systemctl list-unit-files'.

   local systemctl_output_without_legend systemctl_output_with_legend

   systemctl_output_without_legend="$($machine_command_machine 2>&1)" || true

   if [ "$systemctl_output_without_legend" = "" ]; then
      local MSG
      MSG="<p>systemd '<u>$check_type</u>' units check result: OK."
      if [ "$verbose" -ge "1" ]; then
         $output_x ${output_opts[@]} --messagex --typex "info" --message "$MSG"
         $output_cli ${output_opts[@]} --messagecli --typecli "info" --message "$MSG"
      fi
      return 0
   fi

   systemctl_output_with_legend="$($machine_command_pretty 2>&1)" || true
   systemctl_output_with_legend="$(br_add "$systemctl_output_with_legend")"

   local if_you_know_what_you_are_doing_msg
   if_you_know_what_you_are_doing_msg="$(if_you_know_what_you_are_doing_funct "$FUNCNAME")"

   local MSG
   MSG="<p>systemd '<u>$check_type</u>' units check result: One or more units failed to load.
<br />
<br />A possible system issue was detected. There is no need to panic. This is not a serious security problem. However, it may impact other system checks.
<br />
<br />Some systemd '<u>$check_type</u>' units may be stuck in a failed, activating, or deactivating state.
<br />
Output of '<code>$machine_command_pretty</code>':$manual_check_equivalent
<br></br>
<br></br>########################################
<br></br><code>$systemctl_output_with_legend</code>
<br></br>########################################
<br></br>
<br></br>To check manually:
<br></br>1. Open a terminal. ($start_menu_instructions_system_first_part Terminal)
<br></br>2. Run: <blockquote>
<code>$machine_command_pretty</code></blockquote>

$if_you_know_what_you_are_doing_msg</p>"
   $output_x ${output_opts[@]} --messagex --typex "warning" --message "$MSG"
   $output_cli ${output_opts[@]} --messagecli --typecli "warning" --message "$MSG"

   EXIT_CODE="1"

   return 0
}

check_services() {
   check_type="system"
   manual_check_equivalent="<br /> (equivalent to: <code>sudo systemctl --state=failed,activating,deactivating list-units</code>)"
   machine_command_machine="leaprun read-systemctl-logs-failed-units"
   machine_command_pretty="leaprun read-systemctl-logs-failed-units-pretty"
   systemcheck_run_function check_services_do

   check_type="user"
   manual_check_equivalent=""
   machine_command_machine="systemctl --user --no-legend --no-pager --no-block --state=failed,activating,deactivating list-units"
    machine_command_pretty="systemctl --user --no-pager --no-block --state=failed,activating,deactivating list-units"
   systemcheck_run_function check_services_do
}

print_critical_output_message_maybe() {
   local critical_output chosen_boot if_you_know_what_you_are_doing_msg
   critical_output="${1:-}"
   chosen_boot="${2:-}"
   if_you_know_what_you_are_doing_msg="${3:-}"

   if [ -n "$critical_output" ]; then
      ## Messages such as:
      ## Bad RAM detected
      ## self-detected stall on CPU
      ## are considered severe and can lead to various follow-up issues.
      EXIT_CODE="1"

      MSG="\
<p>systemd critical journal messages check result (<u>${chosen_boot}</u> boot): Possible system issue detected. Some unusual entries were found in the kernel log.
<br />
<br />There is no need to panic. This is not a serious security issue. However, it may affect other system checks. These messages might indicate hardware or software problems.
<br />
<br />Detected messages:
<br /><code>$critical_output</code>
<br></br>$if_you_know_what_you_are_doing_msg</p>"
      $output_x ${output_opts[@]} --messagex --typex "error" --message "$MSG"
      $output_cli ${output_opts[@]} --messagecli --typecli "error" --message "$MSG"

      if [ "$systemcheck_virtualizer_detected" = "oracle" ]; then
         if printf '%s\n' "$critical_output" | grep --ignore-case --fixed-strings -- "self-detected stall on CPU" >/dev/null; then
            MSG="\
<p>systemd critical journal messages check result (<u>${chosen_boot}</u> boot): systemd journal contains <code>self-detected stall on CPU</code>.
<br />
<br />VirtualBox has been identified as your virtualizer (virtual machine software).
<br />
<br />Guess: Are you using VirtualBox on a Windows host operating system? If so, you might be experiencing the \"VirtualBox green turtle issue.\" For more information and how to resolve it, see: <a href=${PROJECT_HOMEPAGE}/wiki/VirtualBox/Green_Turtle_Issue>${PROJECT_HOMEPAGE}/wiki/VirtualBox/Green_Turtle_Issue</a>
<br></br>$if_you_know_what_you_are_doing_msg</p>"
            $output_x ${output_opts[@]} --messagex --typex "error" --message "$MSG"
            $output_cli ${output_opts[@]} --messagecli --typecli "error" --message "$MSG"
         fi
      fi
   else
      MSG="<p>systemd critical journal messages check result (<u>${chosen_boot}</u> boot): OK.</p>"
      if [ "$verbose" -ge "1" ]; then
         $output_x ${output_opts[@]} --messagex --typex "info" --message "$MSG"
         $output_cli ${output_opts[@]} --messagecli --typecli "info" --message "$MSG"
      fi
   fi
}

print_verbose_log_output_message_maybe() {
   local log_output chosen_boot if_you_know_what_you_are_doing_msg
   log_output="${1:-}"
   chosen_boot="${2:-}"
   if_you_know_what_you_are_doing_msg="${3:-}"

   if [ -n "$log_output" ]; then
      EXIT_CODE="1"

      MSG="<p>systemd journal check result (<u>${chosen_boot}</u> boot):
<br></br>########################################
<br></br><code>$log_output</code>
<br></br>########################################
<br></br>$if_you_know_what_you_are_doing_msg</p>"

      $output_x ${output_opts[@]} --messagex --typex "warning" --message "$MSG"
      $output_cli ${output_opts[@]} --messagecli --typecli "warning" --message "$MSG"
   else
      MSG="<p>systemd journal check result (<u>${chosen_boot}</u> boot): OK.</p>"
      $output_x ${output_opts[@]} --messagex --typex "info" --message "$MSG"
      $output_cli ${output_opts[@]} --messagecli --typecli "info" --message "$MSG"
   fi
}

check_journal() {
   local log_output last_boot_log_output critical_output \
      last_boot_critical_output MSG

   ## NOTE: The order of the next for lines of code MUST be preserved.
   ##
   ## 'log-checker check_critical_logs' filters log lines from a file written
   ## by a prior 'log-checker check_service_logs' call. Therefore to get
   ## critical logs from this boot, we must check critical logs after getting
   ## logs for the current boot. To get critical logs from the last boot, we
   ## must check critical logs after getting logs for the last boot.

   ## The contents of these four variables will have been sanitized by
   ## stcatn already.
   log_output="$(leaprun log-checker-check_service_logs_this_boot)" || true
   critical_output="$(leaprun log-checker-check_critical_logs)" || true
   last_boot_log_output="$(leaprun log-checker-check_service_logs_last_boot)" || true
   last_boot_critical_output="$(leaprun log-checker-check_critical_logs)" || true

   local if_you_know_what_you_are_doing_msg
   if_you_know_what_you_are_doing_msg="$(if_you_know_what_you_are_doing_funct "$FUNCNAME")"

   print_critical_output_message_maybe \
      "${critical_output}" \
      'current' \
      "${if_you_know_what_you_are_doing_msg}"
   print_critical_output_message_maybe \
      "${last_boot_critical_output}" \
      'previous' \
      "${if_you_know_what_you_are_doing_msg}"

   if [ "$verbose" -lt "1" ]; then
      return 0
   fi

   print_verbose_log_output_message_maybe \
      "${log_output}" \
      'current' \
      "${if_you_know_what_you_are_doing_msg}"
   print_verbose_log_output_message_maybe \
      "${last_boot_log_output}" \
      'previous' \
      "${if_you_know_what_you_are_doing_msg}"

   return 0
}

check_whonix_firewall_systemd_status() {
   if [ "$vm_lower_case_short" = "machine" ]; then
      return 0
   fi

   local extra_text systemctl_status_whonix_firewall_exit_code systemctl_status_whonix_firewall_output

   if [ -e /run/anon-firewall/failed.status ]; then
      anon_firewall_failed_file_exists=true
      extra_text="Failure file <code>/run/anon-firewall/failed.status</code> exists!"
   else
      anon_firewall_failed_file_exists=false
      extra_text="Failure file <code>/run/anon-firewall/failed.status</code> does not exist, OK."
   fi

   systemctl_status_whonix_firewall_exit_code="0"
   systemctl_status_whonix_firewall_output="$(leaprun whonix-firewall-status 2>&1)" || { systemctl_status_whonix_firewall_exit_code="$?" ; true; }
   systemctl_status_whonix_firewall_output="$(br_add "$systemctl_status_whonix_firewall_output")"

   if [ "$systemctl_status_whonix_firewall_exit_code" = "0" ] && [ "$anon_firewall_failed_file_exists" = "false" ]; then
      local MSG="<p>Whonix firewall systemd unit check result: OK.</p>"
      if [ "$verbose" -ge "1" ]; then
         $output_x ${output_opts[@]} --messagex --typex "info" --message "$MSG"
         $output_cli ${output_opts[@]} --messagecli --typecli "info" --message "$MSG"
      fi
      return 0
   fi

   journalctl_whonix_firewall_output="$(leaprun read-journalctl-logs-whonix-firewall)" || true
   journalctl_whonix_firewall_output="$(br_add "$journalctl_whonix_firewall_output")"

   local if_you_know_what_you_are_doing_msg
   if_you_know_what_you_are_doing_msg="$(if_you_know_what_you_are_doing_funct "$FUNCNAME")"

   local MSG="<p>whonix_firewall failed to load!
<br></br>
<br></br>The whonix_firewall failed to load for an unknown reason. This might be due to a misconfiguration or a race condition. Try restarting the VM to see if the issue persists.
<br></br>
<br></br>$extra_text
<br></br>
<br></br>Output of <code>systemctl status whonix-firewall</code>:
<br></br>
<br></br>########################################
<br></br><code>$systemctl_status_whonix_firewall_output</code>
<br></br>########################################
<br></br>
<br></br>Output of <code>journalctl --boot -u whonix-firewall</code>:
<br></br>
<br></br>########################################
<br></br><code>$journalctl_whonix_firewall_output</code>
<br></br>########################################
<br></br>
<br></br>To investigate manually:
<br></br>1. Open a terminal. ($start_menu_instructions_system_first_part Terminal)
<br></br>2. Run:
<blockquote><code>sudo systemctl status whonix-firewall</code></blockquote>
<br></br>2. Also run:
<blockquote><code>sudo journalctl --boot -u whonix-firewall | cat</code></blockquote>
<br></br>3. Try to manually start the Whonix firewall:
<blockquote><code>sudo whonix_firewall</code></blockquote>

$if_you_know_what_you_are_doing_msg
</p>"

   $output_x ${output_opts[@]} --messagex --typex "error" --message "$MSG"
   $output_cli ${output_opts[@]} --messagecli --typecli "error" --message "$MSG"

   EXIT_CODE="1"
   return 0
}
