/lib/lsb/init-functions文件解析

零、背景

在玩AppArmor的时候涉及到了/etc/init.d/apparmor(无论是sudo /etc/init.d/apparmor start还是sudo systemctl start apparmor.service),而这个文件又涉及到了另一个文件、也就是本文的主角:/lib/lsb/init-functions。

执行sudo /etc/init.d/apparmor start命令时结果如下:

$ sudo /etc/init.d/apparmor start
/etc/init.d/apparmor:行43: /lib/lsb/init-functions: 没有那个文件或目录

而执行sudo systemctl start apparmor.services时结果如下:

$ sudo systemctl start apparmor
Job for apparmor.service failed because the control process exited with error code.
See "systemctl status apparmor.service" and "journalctl -xeu apparmor.service" for details.

通过systemctl status apparmor.service查看状态,结果如下:

$ systemctl status apparmor.service
× apparmor.service - LSB: AppArmor initialization
     Loaded: loaded (/etc/init.d/apparmor; generated)
     Active: failed (Result: exit-code) since Thu 2023-04-20 16:29:35 CST; 1min 29s ago
       Docs: man:systemd-sysv-generator(8)
    Process: 2489718 ExecStart=/etc/init.d/apparmor start (code=exited, status=1/FAILURE)
        CPU: 8ms


$ sudo systemctl status apparmor.service
× apparmor.service - LSB: AppArmor initialization
     Loaded: loaded (/etc/init.d/apparmor; generated)
     Active: failed (Result: exit-code) since Thu 2023-04-20 16:29:35 CST; 1min 39s ago
       Docs: man:systemd-sysv-generator(8)
    Process: 2489718 ExecStart=/etc/init.d/apparmor start (code=exited, status=1/FAILURE)
        CPU: 8ms

4月 20 16:29:35 Ding-Perlis-MP260S48 systemd[1]: Starting LSB: AppArmor initialization...
4月 20 16:29:35 Ding-Perlis-MP260S48 apparmor[2489718]: /etc/init.d/apparmor:行43: /lib/lsb/init-functions: 没有那个文件或目录4月 20 16:29:35 Ding-Perlis-MP260S48 systemd[1]: apparmor.service: Control process exited, code=exited, status=1/FAILURE
4月 20 16:29:35 Ding-Perlis-MP260S48 systemd[1]: apparmor.service: Failed with result 'exit-code'.
4月 20 16:29:35 Ding-Perlis-MP260S48 systemd[1]: Failed to start LSB: AppArmor initialization.

可以看到,失败的根本原因就是没有/lib/lsb/init-functions文件。

先来看一下/etc/init.d/apparmor文件中是怎样与/lib/lsb/init-functions文件联系的。/etc/init.d/apparmor文件片段如下:

APPARMOR_FUNCTIONS=/lib/apparmor/rc.apparmor.functions

# Functions needed by rc.apparmor.functions
 
. /lib/lsb/init-functions
……

# Source AppArmor function library
if [ -f "${APPARMOR_FUNCTIONS}" ]; then
    . ${APPARMOR_FUNCTIONS}
else
    aa_log_failure_msg "Unable to find AppArmor initscript functions"
    exit 1
fi

通过以上代码片段就可以看出,如果/lib/lsb/init-functions文件,则/etc/init.d/apparmor的第43行代码. /lib/lsb/init-functions就会导致以上错误。

那么这个/lib/lsb/init-fiunctions到底是何方神圣?其作用和内容又是怎样的?接下来就进行完全解析。

一、简介

根据ChatGPT的搜索结果(poe.com/ChatGPT),/lib/lsb/init-functions文件的介绍如下:

/lib/lsb/init-functions文件是一个Linux系统上的脚本文件,它包含了一些常用的函数,用于在系统启动时执行各种任务。这个文件通常被用来编写启动脚本,比如在系统启动时自动启动某个服务。

这个文件提供了许多函数,比如:

  • log_success_msg:用于记录启动成功的消息;

  • log_failure_msg:用于记录启动失败的消息;

  • log_warning_msg:用于记录启动警告的消息;

  • log_progress_msg:用于记录启动进度的消息;

  • pidofproc:用于获取某个进程的PID;

  • killproc:用于杀死某个进程;

  • start_daemon:用于启动一个后台进程。

这些函数可以让脚本编写者更加方便地编写启动脚本,同时也提高了启动脚本的可读性和可维护性。

再来看看/lib/lsb文件夹是干什么的:

/lib/lsb是一个目录,它通常包含了一些用于支持Linux Standard Base(LSB)规范的文件和脚本。LSB是一种标准,旨在使不同的Linux发行版之间更加兼容,从而提高应用程序在各种Linux发行版上的可移植性。
在/lib/lsb目录中,常见的文件和目录包括:

  • init-functions:包含了一些常用的函数,用于在系统启动时执行各种任务,如启动服务等;
  • lsb_release:用于显示当前系统的LSB版本和发行版信息;
  • lsb_log_message:用于在系统日志中记录LSB兼容的日志信息;
  • /etc/lsb-release:包含了当前系统的LSB版本和发行版信息,可以被其他脚本和程序引用。

这些文件和脚本可以帮助系统管理员编写LSB兼容的启动脚本和管理工具,从而提高系统的可移植性和可维护性。

笔者的Ubuntu系统(Ubuntu 22.04.1 LTS)电脑中的/lib/lsb/目录下倒是没有那么多文件和文件夹,如下:

$ ls /lib/lsb/
init-functions  init-functions.d

$ tree /lib/lsb
/lib/lsb
├── init-functions
└── init-functions.d
    ├── 00-verbose
    ├── 40-systemd
    ├── 50-ubuntu-logging
    └── 99-plymouth

1 directory, 5 files

二、代码分析

通过上边介绍,已经对/lib/lsb/init-functions文件有了大致的了解。趁热打铁,结合概述看一下其源码:

# /lib/lsb/init-functions for Debian -*- shell-script -*-
#
#Copyright (c) 2002-08 Chris Lawrence
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions
#are met:
#1. Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
#2. Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
#3. Neither the name of the author nor the names of other contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
#IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
#BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
#OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
#EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

start_daemon () {
    local force nice pidfile exec args OPTIND
    force=""
    nice=0
    pidfile=/dev/null

    OPTIND=1
    while getopts fn:p: opt ; do
        case "$opt" in
            f)  force="force";;
            n)  nice="$OPTARG";;
            p)  pidfile="$OPTARG";;
        esac
    done
    
    shift $(($OPTIND - 1))
    if [ "$1" = '--' ]; then
        shift
    fi

    exec="$1"; shift

    args="--start --nicelevel $nice --quiet --oknodo"
    if [ "$force" ]; then
        /sbin/start-stop-daemon $args \
	    --chdir "$PWD" --startas $exec --pidfile /dev/null -- "$@"
    elif [ $pidfile ]; then
        /sbin/start-stop-daemon $args \
	    --chdir "$PWD" --exec $exec --oknodo --pidfile "$pidfile" -- "$@"
    else
        /sbin/start-stop-daemon $args --chdir "$PWD" --exec $exec -- "$@"
    fi
}

pidofproc () {
    local pidfile base status specified pid OPTIND
    pidfile=
    specified=
    
    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG"
                specified="specified"
		;;
        esac
    done
    shift $(($OPTIND - 1))
    if [ $# -ne 1 ]; then
        echo "$0: invalid arguments" >&2
        return 4
    fi

    base=${1##*/}
    if [ ! "$specified" ]; then
        pidfile="/var/run/$base.pid"
    fi

    if [ -n "${pidfile:-}" ]; then
     if [ -e "$pidfile" ]; then
      if [ -r "$pidfile" ]; then
        read pid < "$pidfile"
        if [ -n "${pid:-}" ]; then
            if $(kill -0 "${pid:-}" 2> /dev/null); then
                echo "$pid" || true
                return 0
            elif ps "${pid:-}" >/dev/null 2>&1; then
                echo "$pid" || true
                return 0 # program is running, but not owned by this user
            else
                return 1 # program is dead and /var/run pid file exists
            fi
        fi
      else
        return 4 # pid file not readable, hence status is unknown.
      fi
     else
       # pid file doesn't exist, try to find the pid nevertheless
       if [ -x /bin/pidof ] && [ ! "$specified" ]; then
         status="0"
         /bin/pidof -c -o %PPID -x $1 || status="$?"
         if [ "$status" = 1 ]; then
             return 3 # program is not running
         fi
         return 0
       fi
       return 3 # specified pid file doesn't exist, program probably stopped
     fi
    fi
    if [ "$specified" ]; then
        return 3 # almost certain it's not running
    fi
    return 4 # Unable to determine status
}

# start-stop-daemon uses the same algorithm as "pidofproc" above.
killproc () {
    local pidfile sig status base name_param is_term_sig OPTIND
    pidfile=
    name_param=
    is_term_sig=

    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG";;
        esac
    done
    shift $(($OPTIND - 1))

    base=${1##*/}
    if [ ! $pidfile ]; then
        name_param="--name $base --pidfile /var/run/$base.pid"
    else
        name_param="--name $base --pidfile $pidfile"
    fi

    sig=$(echo ${2:-} | sed -e 's/^-\(.*\)/\1/')
    sig=$(echo $sig | sed -e 's/^SIG\(.*\)/\1/')
    if [ "$sig" = 15 ] || [ "$sig" = TERM ]; then
        is_term_sig="terminate_signal"
    fi
    status=0
    if [ ! "$is_term_sig" ]; then
        if [ -n "$sig" ]; then
            /sbin/start-stop-daemon --stop --signal "$sig" \
		--quiet $name_param || status="$?"
        else
            /sbin/start-stop-daemon --stop \
		--retry 5 \
		--quiet $name_param || status="$?"
        fi
    else
        /sbin/start-stop-daemon --stop --quiet \
	    --oknodo $name_param || status="$?"
    fi
    if [ "$status" = 1 ]; then
        if [ -z "$sig" ]; then
            return 0
        fi
        return 3 # program is not running
    fi

    if [ "$status" = 0 ] && [ "$is_term_sig" ] && [ "$pidfile" ]; then
        pidofproc -p "$pidfile" "$1" >/dev/null || rm -f "$pidfile"
    fi
    return 0
}

# Return LSB status
status_of_proc () {
    local pidfile daemon name status OPTIND

    pidfile=
    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG";;
        esac
    done
    shift $(($OPTIND - 1))

    if [ -n "$pidfile" ]; then
        pidfile="-p $pidfile"
    fi
    daemon="$1"
    name="$2"

    status="0"
    pidofproc $pidfile $daemon >/dev/null || status="$?"
    if [ "$status" = 0 ]; then
        log_success_msg "$name is running"
        return 0
    elif [ "$status" = 4 ]; then
        log_failure_msg "could not access PID file for $name"
        return $status
    else
        log_failure_msg "$name is not running"
        return $status
    fi
}

log_use_fancy_output () {
    TPUT=/usr/bin/tput
    EXPR=/usr/bin/expr
    if  [ -t 1 ] &&
	[ "x${TERM:-}" != "x" ] &&
	[ "x${TERM:-}" != "xdumb" ] &&
	[ -x $TPUT ] && [ -x $EXPR ] &&
	$TPUT hpa 60 >/dev/null 2>&1 &&
	$TPUT setaf 1 >/dev/null 2>&1
    then
        [ -z $FANCYTTY ] && FANCYTTY=1 || true
    else
        FANCYTTY=0
    fi
    case "$FANCYTTY" in
        1|Y|yes|true)   true;;
        *)              false;;
    esac
}

log_success_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@
    fi
    log_end_msg 0
}

log_failure_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@ "..."
    fi
    log_end_msg 1 || true
}

log_warning_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@ "..."
    fi
    log_end_msg 255 || true
}

#
# NON-LSB HELPER FUNCTIONS
#
# int get_lsb_header_val (char *scriptpathname, char *key)
get_lsb_header_val () {
        if [ ! -f "$1" ] || [ -z "${2:-}" ]; then
                return 1
        fi
        LSB_S="### BEGIN INIT INFO"
        LSB_E="### END INIT INFO"
        sed -n "/$LSB_S/,/$LSB_E/ s/# $2: \+\(.*\)/\1/p" "$1"
}

# If the currently running init daemon is upstart, return zero; if the
# calling init script belongs to a package which also provides a native
# upstart job, it should generally exit non-zero in this case.
init_is_upstart()
{
   if [ -x /sbin/initctl ] && /sbin/initctl version 2>/dev/null | /bin/grep -q upstart; then
       return 0
   fi
   return 1
}

# int log_begin_message (char *message)
log_begin_msg () {
    log_begin_msg_pre "$@"
    if [ -z "${1:-}" ]; then
        return 1
    fi
    echo -n "$@" || true
    log_begin_msg_post "$@"
}

# Sample usage:
# log_daemon_msg "Starting GNOME Login Manager" "gdm"
#
# On Debian, would output "Starting GNOME Login Manager: gdm"
# On Ubuntu, would output " * Starting GNOME Login Manager..."
#
# If the second argument is omitted, logging suitable for use with
# log_progress_msg() is used:
#
# log_daemon_msg "Starting remote filesystem services"
#
# On Debian, would output "Starting remote filesystem services:"
# On Ubuntu, would output " * Starting remote filesystem services..."

log_daemon_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    log_daemon_msg_pre "$@"

    if [ -z "${2:-}" ]; then
        echo -n "$1:" || true
        return
    fi
    
    echo -n "$1: $2" || true
    log_daemon_msg_post "$@"
}

# #319739
#
# Per policy docs:
#
#     log_daemon_msg "Starting remote file system services"
#     log_progress_msg "nfsd"; start-stop-daemon --start --quiet nfsd
#     log_progress_msg "mountd"; start-stop-daemon --start --quiet mountd
#     log_progress_msg "ugidd"; start-stop-daemon --start --quiet ugidd
#     log_end_msg 0
#
# You could also do something fancy with log_end_msg here based on the
# return values of start-stop-daemon; this is left as an exercise for
# the reader...
#
# On Ubuntu, one would expect log_progress_msg to be a no-op.
log_progress_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    echo -n " $@" || true
}


# int log_end_message (int exitstatus)
log_end_msg () {
    # If no arguments were passed, return
    if [ -z "${1:-}" ]; then
        return 1
    fi

    local retval
    retval=$1

    log_end_msg_pre "$@"

    # Only do the fancy stuff if we have an appropriate terminal
    # and if /usr is already mounted
    if log_use_fancy_output; then
        RED=$( $TPUT setaf 1)
        YELLOW=$( $TPUT setaf 3)
        NORMAL=$( $TPUT op)
    else
        RED=''
        YELLOW=''
        NORMAL=''
    fi

    if [ $1 -eq 0 ]; then
        echo "." || true
    elif [ $1 -eq 255 ]; then
        /bin/echo -e " ${YELLOW}(warning).${NORMAL}" || true
    else
        /bin/echo -e " ${RED}failed!${NORMAL}" || true
    fi
    log_end_msg_post "$@"
    return $retval
}

log_action_msg () {
    log_action_msg_pre "$@"
    echo "$@." || true
    log_action_msg_post "$@"
}

log_action_begin_msg () {
    log_action_begin_msg_pre "$@"
    echo -n "$@..." || true
    log_action_begin_msg_post "$@"
}

log_action_cont_msg () {
    echo -n "$@..." || true
}

log_action_end_msg () {
    local end
    log_action_end_msg_pre "$@"
    if [ -z "${2:-}" ]; then
        end="."
    else
        end=" ($2)."
    fi

    if [ $1 -eq 0 ]; then
        echo "done${end}" || true
    else
        if log_use_fancy_output; then
            RED=$( $TPUT setaf 1)
            NORMAL=$( $TPUT op)
            /bin/echo -e "${RED}failed${end}${NORMAL}" || true
        else
            echo "failed${end}" || true
        fi
    fi
    log_action_end_msg_post "$@"
}

# Pre&Post empty function declaration, to be overriden from /lib/lsb/init-functions.d/*
log_daemon_msg_pre () { :; }
log_daemon_msg_post () { :; }
log_begin_msg_pre () { :; }
log_begin_msg_post () { :; }
log_end_msg_pre () { :; }
log_end_msg_post () { :; }
log_action_msg_pre () { :; }
log_action_msg_post () { :; }
log_action_begin_msg_pre () { :; }
log_action_begin_msg_post () { :; }
log_action_end_msg_pre () { :; }
log_action_end_msg_post () { :; }

# Include hooks from other packages in /lib/lsb/init-functions.d
for hook in $(run-parts --lsbsysinit --list /lib/lsb/init-functions.d 2>/dev/null); do
    [ -r $hook ] && . $hook || true
done

FANCYTTY=
[ -e /etc/lsb-base-logging.sh ] && . /etc/lsb-base-logging.sh || true

本文只关注与/etc/init.d/apparmor相关的函数。下边逐一进行分析:

  • log_daemon_msg函数

代码如下:

# Sample usage:
# log_daemon_msg "Starting GNOME Login Manager" "gdm"
#
# On Debian, would output "Starting GNOME Login Manager: gdm"
# On Ubuntu, would output " * Starting GNOME Login Manager..."
#
# If the second argument is omitted, logging suitable for use with
# log_progress_msg() is used:
#
# log_daemon_msg "Starting remote filesystem services"
#
# On Debian, would output "Starting remote filesystem services:"
# On Ubuntu, would output " * Starting remote filesystem services..."

log_daemon_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    log_daemon_msg_pre "$@"

    if [ -z "${2:-}" ]; then
        echo -n "$1:" || true
        return
    fi
    
    echo -n "$1: $2" || true
    log_daemon_msg_post "$@"
}
  • log_end_msg函数

代码如下:

# int log_end_message (int exitstatus)
log_end_msg () {
    # If no arguments were passed, return
    if [ -z "${1:-}" ]; then
        return 1
    fi

    local retval
    retval=$1

    log_end_msg_pre "$@"

    # Only do the fancy stuff if we have an appropriate terminal
    # and if /usr is already mounted
    if log_use_fancy_output; then
        RED=$( $TPUT setaf 1)
        YELLOW=$( $TPUT setaf 3)
        NORMAL=$( $TPUT op)
    else
        RED=''
        YELLOW=''
        NORMAL=''
    fi

    if [ $1 -eq 0 ]; then
        echo "." || true
    elif [ $1 -eq 255 ]; then
        /bin/echo -e " ${YELLOW}(warning).${NORMAL}" || true
    else
        /bin/echo -e " ${RED}failed!${NORMAL}" || true
    fi
    log_end_msg_post "$@"
    return $retval
}
  • log_daemon_msg_pre、log_daemon_msg_post、log_end_msg_pre、log_end_msg_post

log_daemon_msg_pre、log_daemon_msg_post、log_end_msg_pre、log_end_msg_post等函数在同文件(/lib/lsb/init-functions)中,如下:

# Pre&Post empty function declaration, to be overriden from /lib/lsb/init-functions.d/*
log_daemon_msg_pre () { :; }
log_daemon_msg_post () { :; }
log_begin_msg_pre () { :; }
log_begin_msg_post () { :; }
log_end_msg_pre () { :; }
log_end_msg_post () { :; }
log_action_msg_pre () { :; }
log_action_msg_post () { :; }
log_action_begin_msg_pre () { :; }
log_action_begin_msg_post () { :; }
log_action_end_msg_pre () { :; }
log_action_end_msg_post () { :; }

根据注释,这些函数在/lib/lsb/init-functions.d/下重载,没有重载的函数就是使用以上默认的空函数。笔者电脑上/lib/lsb/init-functions.d/下的内容如下(上边实际上已经列出了):

$ ls /lib/lsb/init-functions.d/
00-verbose  40-systemd  50-ubuntu-logging  99-plymouth

这里只列出50-ubuntu-logging文件的内容,如下:

# Default init script logging functions suitable for Ubuntu.
# See /lib/lsb/init-functions for usage help.
LOG_DAEMON_MSG=""

log_use_plymouth () {
    if [ "${loop:-n}" = y ]; then
        return 1
    fi
    plymouth --ping >/dev/null 2>&1
}

log_success_msg () {
    echo " * $@" || true
}

log_failure_msg () {
    if log_use_fancy_output; then
        RED=`$TPUT setaf 1`
        NORMAL=`$TPUT op`
        echo " $RED*$NORMAL $@" || true
    else
        echo " * $@" || true
    fi
}

log_warning_msg () {
    if log_use_fancy_output; then
        YELLOW=`$TPUT setaf 3`
        NORMAL=`$TPUT op`
        echo " $YELLOW*$NORMAL $@" || true
    else
        echo " * $@" || true
    fi
}

log_begin_msg () {
    log_daemon_msg "$1"
}

log_daemon_msg () {
    if [ -z "$1" ]; then
        return 1
    fi

    if log_use_fancy_output && $TPUT xenl >/dev/null 2>&1; then
        COLS=`$TPUT cols`
        if [ "$COLS" ] && [ "$COLS" -gt 6 ]; then
            COL=`$EXPR $COLS - 7`
        else
            COLS=80
            COL=73
        fi

        if log_use_plymouth; then
            # If plymouth is running, don't output anything at this time
            # to avoid buffering problems (LP: #752393)
            if [ -z "$LOG_DAEMON_MSG" ]; then
                LOG_DAEMON_MSG=$*
                return
            fi
        fi

        # We leave the cursor `hanging' about-to-wrap (see terminfo(5)
        # xenl, which is approximately right). That way if the script
        # prints anything then we will be on the next line and not
        # overwrite part of the message.

        # Previous versions of this code attempted to colour-code the
        # asterisk but this can't be done reliably because in practice
        # init scripts sometimes print messages even when they succeed
        # and we won't be able to reliably know where the colourful
        # asterisk ought to go.

        printf " * $*       " || true
        # Enough trailing spaces for ` [fail]' to fit in; if the message
        # is too long it wraps here rather than later, which is what we
        # want.
        $TPUT hpa `$EXPR $COLS - 1` || true
        printf ' ' || true
    else
        echo " * $@" || true
        COL=
    fi
}

log_progress_msg () {
    :
}

log_end_msg () {
    if [ -z "$1" ]; then
        return 1
    fi

    if [ "$COL" ] && [ -x "$TPUT" ]; then
        # If plymouth is running, print previously stored output
        # to avoid buffering problems (LP: #752393)
        if log_use_plymouth; then
            if [ -n "$LOG_DAEMON_MSG" ]; then
                log_daemon_msg $LOG_DAEMON_MSG
                LOG_DAEMON_MSG=""
            fi
        fi

        printf "\r" || true
        $TPUT hpa $COL
        if [ "$1" -eq 0 ]; then
            echo "[ OK ]" || true
        else
            printf '[' || true
            $TPUT setaf 1 || true # red
            printf fail || true
            $TPUT op || true # normal
            echo ']' || true
        fi
    else
        if [ "$1" -eq 0 ]; then
            echo "   ...done." || true
        else
            echo "   ...fail!" || true
        fi
    fi
    return $1
}

log_action_msg () {
    echo " * $@" || true
}

log_action_begin_msg () {
    log_daemon_msg "$@..." || true
}

log_action_cont_msg () {
    log_daemon_msg "$@..." || true
}

log_action_end_msg () {
    # In the future this may do something with $2 as well.
    log_end_msg "$1" || true
}

就分析到这里吧,不再深入了。

猜你喜欢

转载自blog.csdn.net/phmatthaus/article/details/130269639