Add isu status option 31/299031/26
authorAdam Michalski <a.michalski2@partner.samsung.com>
Mon, 18 Sep 2023 15:01:50 +0000 (17:01 +0200)
committerAdam Michalski <a.michalski2@partner.samsung.com>
Fri, 6 Oct 2023 09:51:17 +0000 (11:51 +0200)
Change-Id: I56b1c5c9b676d9a4da8685db35a4a92867516b80

packaging/isu.spec
src/pkg_manager/isu

index adbeab8..a0fff1d 100644 (file)
@@ -1,6 +1,6 @@
 Summary: Individual Service Upgrade support
 Name:    isu
-Version: 8.0.14
+Version: 8.0.15
 Release: 1
 Source0: %{name}-%{version}.tar.gz
 License: MIT
index c6fbd56..d8aad73 100755 (executable)
@@ -4,6 +4,8 @@ set -u
 
 PATH=/bin:/usr/bin:/sbin:/usr/sbin
 ISU_PKGS_PATH=/opt/isu
+ISU_RUN_PATH=/run/isu
+ISU_CFGS_PATH=/etc/isu
 
 readonly PKG_FILE_NOT_EXIST=100
 readonly PKG_INSTALL_ERROR=101
@@ -36,6 +38,20 @@ function print_install_help()
     echo "                               (for development purposes only)"
 }
 
+function print_status_help()
+{
+    if [ -z "${1:-}" ]; then
+        echo ""
+        echo "Usage:"
+        echo -n "  $0"
+    fi
+
+    echo " status [-v] [<pkg_name>]    - show systemd service status. If no pkg_name given,"
+    echo "                show status for all isu services."
+    echo "         -v   - verbose mode: show sandboxing mechanism, mount files status etc."
+    echo "                (output format subject to change, do not parse in scripts)"
+}
+
 function print_help()
 {
     echo ""
@@ -46,6 +62,7 @@ function print_help()
     echo
     echo "commands:"
     echo " list                        - list installed packages"
+    print_status_help x
     print_install_help x
     print_remove_help x
 }
@@ -63,6 +80,220 @@ function list_pkgs()
     done
 }
 
+function query_systemd_mount_files()
+{
+    local mount_files=$(compgen -G "$ISU_PKGS_PATH/$pkg_name/system-services/*.mount" 2>/dev/null)
+    if [ -z "$mount_files" ]; then
+        is_systemd_mount_file_sandboxing=0
+        return
+    fi
+    # there may be more than one mount file - check all of them, but only in verbose mode
+    if [ "$verbose" == 1 ]; then
+        for m_f in $mount_files; do
+            mount_file_name=$(basename "$m_f")
+            local detailed_mount_state=$(systemctl status "$mount_file_name" 2>&1)
+            grep -q 'Active: active (mounted)' <<< "$detailed_mount_state" || {
+                all_mounts_successful=0
+                break
+        }
+        done
+    fi
+}
+
+function show_sandboxing_method()
+{
+    local no_sandboxing=0
+    if [ "$is_isu_sandboxed" == "0" ] && [ "$is_systemd_mount_file_sandboxing" == "0" ] && [ "$is_systemd_bind_paths" == "0" ]; then
+        no_sandboxing=1
+    fi
+
+    sandboxing_method=""
+    echo -n "sandboxing: "
+    if [ "$is_isu_sandboxed" == "1" ]; then
+        sandboxing_method+="isu-sandbox,"
+    fi
+    if [ "$is_systemd_mount_file_sandboxing" == "1" ]; then
+        if [ "$all_mounts_successful" == "1" ]; then
+            sandboxing_method+="systemd mount files,"
+        else
+            sandboxing_method+="systemd mount files [WARNING: not all mount files active],"
+        fi
+    fi
+    if [ "$is_systemd_bind_paths" == "1" ]; then
+        sandboxing_method+="systemd BindPaths/TemporaryFileSystem"
+    fi
+    if [ "$no_sandboxing" == "1" ]; then
+        sandboxing_method+="no sandboxing"
+    fi
+
+    echo "${sandboxing_method%%,}"
+}
+
+function show_service_status_details()
+{
+    local user_name="$1"
+
+    # query systemd for service state and determine sandboxing method
+    if [ "$user_name" == "root" ]; then
+        local svc_state=$(systemctl show -p SubState --value "$svc_name" 2>&1)
+    else
+        local svc_state=$(su - "$user" -c "systemctl --user show -p SubState --value \"$svc_name\"" 2>&1)
+    fi
+
+    local is_generated=1
+    local is_isu_sandboxed=1
+    local is_systemd_mount_file_sandboxing=1
+    local is_systemd_bind_paths=1
+    local all_mounts_successful=1
+    local is_svc_installed=1
+
+    local detailed_svc_state
+    if [ "$user_name" == "root" ]; then
+        local detailed_svc_state=$(systemctl status "$svc_name" 2>&1)
+    else
+        local detailed_svc_state=$(su - "$user" -c "systemctl --user status \"$svc_name\"" 2>&1)
+    fi
+    grep -qv 'Unit .*\.service could not be found' <<< "$detailed_svc_state" || {
+        echo "Service file: $svc_name.service not found"
+        return
+    }
+
+    if [ "$user_name" == "root" ]; then
+        grep -q 'loaded (/run/systemd/generator' <<< "$detailed_svc_state" || is_generated=0
+    else
+        grep -q 'loaded (/run/user/.*; generated)' <<< "$detailed_svc_state" || is_generated=0
+    fi
+
+    # determine sandboxing method
+    grep -q '/bin/isu-sandbox' <<< "$detailed_svc_state" || is_isu_sandboxed=0
+
+    if [ "$user_name" == "root" ]; then
+        local svc_file=$(systemctl cat "$svc_name" 2>&1)
+    else
+        local svc_file=$(su - "$user" -c "systemctl --user cat \"$svc_name\"" 2>&1)
+    fi
+
+    if [ "$is_isu_sandboxed" == 0 ]; then
+        grep -q '/bin/isu-sandbox' <<< "$svc_file" && is_isu_sandboxed=1
+    fi
+
+    query_systemd_mount_files
+
+    grep -q 'BindPaths\|BindReadOnlyPaths\|TemporaryFileSystem' <<< "$svc_file" || is_systemd_bind_paths=0
+
+    # do not show status for `*.mount.service` file
+    if [ -z "${svc_name##*.mount}" ]; then
+        return
+    fi
+
+    # show isu package info, status and sandboxing method (optional)
+    echo -n "$pkg_name "
+    if [ -n "$version" ]; then
+        echo -n "($version) "
+    fi
+
+    if [ -n "$sys_version" ] && [ "$verbose" == "1" ]; then
+        echo -n "(system package version: $sys_version) "
+    fi
+
+    echo -n "[$svc_name.service] "
+    if [ "$user_name" != "root" ]; then
+        echo -n "[user: $user]: "
+    fi
+
+    echo -n "$svc_state"
+
+    if [ "$verbose" != 1 ]; then
+        echo ""
+        return
+    fi
+
+    if [ "$is_generated" == "1" ]; then
+        echo -n ", "
+        show_sandboxing_method
+    fi
+}
+
+function show_service_status_single()
+{
+    IFS=' ' read -r -a svc_names_arr <<< "$2"
+
+    for i in "${svc_names_arr[@]}"; do
+        if [ -z "$i" ] || [ -z "${i%%service}" ]; then
+            continue
+        fi
+        svc_name=${i%%.service}
+        show_service_status_details "$1"
+    done
+}
+
+function show_system_service_status_single()
+{
+    show_service_status_single "root" "$@"
+}
+
+function show_user_service_status_single()
+{
+    for user in $systemd_users; do
+        show_service_status_single "$user" "$@"
+    done
+}
+
+function isu_status_single()
+{
+    local pkg_name="${1%.service}"
+    local verbose="${2:-0}"
+    local isu_cfg_file=$(cat "$ISU_CFGS_PATH/$pkg_name/isu.cfg" 2>/dev/null)
+    local sys_version=$(grep -e '^version' <<< "$isu_cfg_file" 2>/dev/null | awk -F '=' '{gsub(/[ ]+/,""); print $2}')
+    local isu_run_cfg_file=$(cat "$ISU_RUN_PATH/$pkg_name/isu.cfg" 2>/dev/null)
+    local version=$(grep -e '^version' <<< "$isu_run_cfg_file" 2>/dev/null | awk -F '=' '{gsub(/[ ]+/,""); print $2}')
+    local svc_names=$(grep -e 'system_service\|user_service' <<< "$isu_run_cfg_file" 2>/dev/null | awk -F '=' '{print $2}')
+    local svc_name
+    local is_user_service=1
+
+    svc_names=${svc_names:=${1%.service}}
+    if [ -z "$svc_names" ] || [ -z "$version" ]; then
+        echo "Cannot find the correct isu config file for package: $pkg_name"
+        return
+    fi
+
+    grep -q 'user_service' <<< "$isu_run_cfg_file" || is_user_service=0
+
+    if [ "$is_user_service" == 0 ]; then
+        show_system_service_status_single "$svc_names"
+    else
+        show_user_service_status_single "$svc_names"
+    fi
+}
+
+function query_all_service_statuses()
+{
+    local isu_dirs=$(compgen -G "$ISU_RUN_PATH/*" 2>/dev/null)
+    if [ -z "$isu_dirs" ]; then
+        echo "No isu packages."
+        return
+    fi
+    for pkg_path in $isu_dirs; do
+        local pkg_name=$(basename "$pkg_path")
+        isu_status_single "$pkg_name" "$2"
+    done
+}
+
+function isu_status()
+{
+    local pkg_name="$1"
+    local verbose_flag="$2"
+    local systemd_users=$(ps axo user:100,args | awk '$2 == "/usr/lib/systemd/systemd" && $3 == "--user" { print $1 }')
+
+    case "$1" in
+        all)
+            query_all_service_statuses "$@"
+            ;;
+        *)
+            isu_status_single "$pkg_name" "$verbose_flag"
+    esac
+}
+
 function verify_checksum()
 {
     local pkg_path="$1"
@@ -159,6 +390,32 @@ case $1 in
     list)
         list_pkgs
         ;;
+    status)
+        verbose=0
+        shift 1
+
+        while getopts "v" arg; do
+            case $arg in
+                v)
+                    verbose=1
+                    ;;
+                *)
+                    print_status_help
+                    exit
+                    ;;
+            esac
+        done
+
+        shift $((OPTIND-1))
+
+        if [ $# -eq 1 ]; then
+            isu_status "$1" "$verbose"
+        elif [ $# -eq 0 ]; then
+            isu_status "all" "$verbose"
+        else
+            print_status_help
+        fi
+        ;;
     install)
         force=0
         restart_after_install=0