0

I came across the following scripts and there is something that I cannot fully understand

    #!/bin/sh /etc/rc.common
# Copyright (C) 2006-2011 OpenWrt.org

START=50

start() {
        mkdir -m 0755 -p /var/run/vsftpd
        service_start /usr/sbin/vsftpd
}

stop() {
        service_stop /usr/sbin/vsftpd
}

How is '/etc/rc.common' used here?

here is the contents of rc.common

#!/bin/sh
# Copyright (C) 2006-2011 OpenWrt.org

. $IPKG_INSTROOT/lib/functions.sh
. $IPKG_INSTROOT/lib/functions/service.sh

initscript=$1
action=${2:-help}
shift 2

start() {
        return 0
}

stop() {
        return 0
}

reload() {
        return 1
}

restart() {
        trap '' TERM
        stop "$@"
        start "$@"
}

boot() {
        start "$@"
}

shutdown() {
        stop
}

disable() {
        name="$(basename "${initscript}")"
        rm -f "$IPKG_INSTROOT"/etc/rc.d/S??$name
        rm -f "$IPKG_INSTROOT"/etc/rc.d/K??$name
}

enable() {
        name="$(basename "${initscript}")"
        disable
        [ -n "$START" -o -n "$STOP" ] || {
                echo "/etc/init.d/$name does not have a START or STOP value"
                return 1
        }
        [ "$START" ] && ln -s "../init.d/$name" "$IPKG_INSTROOT/etc/rc.d/S${START}${name##S[0-9][0-9]}"
        [ "$STOP"  ] && ln -s "../init.d/$name" "$IPKG_INSTROOT/etc/rc.d/K${STOP}${name##K[0-9][0-9]}"
}

enabled() {
        name="$(basename "${initscript}")"
        [ -x "$IPKG_INSTROOT/etc/rc.d/S${START}${name##S[0-9][0-9]}" ]
}

depends() {
        return 0
}

help() {
        cat <<EOF
Syntax: $initscript [command]

Available commands:
        start   Start the service
        stop    Stop the service
        restart Restart the service
        reload  Reload configuration files (or restart if that fails)
        enable  Enable service autostart
        disable Disable service autostart
$EXTRA_HELP
EOF
}

. "$initscript"

ALL_COMMANDS="start stop reload restart boot shutdown enable disable enabled depends ${EXTRA_COMMANDS}"
list_contains ALL_COMMANDS "$action" || action=help
[ "$action" = "reload" ] && action='eval reload "$@" || restart "$@" && :'
$action "$@"

Hope some of you could shed light on this. Thank you!

PS:Another thing that i don't quite get is how the functions in the scripts are invoked by simply appending the function name to the full path of the startup scripts. for example, '/etc/init.d/vsftpd test' will execute a function named 'test' in either /etc/init.d/vsftpd or /etc/rc.common.But if 'test' function is defined both in the startup script and /etc/rc.common, the function in the former will be run while the one in rc.common will not.

Also, why is not

'[ "$action" = "reload" ] && action='eval reload "$@" || restart "$@" && :'

simply written as

'[ "$action" = "reload" ] && action='eval reload "$@" || restart "$@"'

Thank you!

5
  • Is your first sample the entire script? Commented Aug 24, 2013 at 14:29
  • yes, i've just randomly picked a simple one! all the startup scripts share the same beginning line #!/bin/sh /etc/rc.common Commented Aug 24, 2013 at 14:59
  • The "hash bang" at the beginning of the file may provide optional arguments to the interpreter. So generally you could have #!/usr/bin/sh [options]. But a script file isn't considered an "option" to the shell, and I haven't seen a case where a shell script is offered as an argument in this manner. I did a quick test case in bash and didn't observe useful behavior (i.e., bash ignored the optional script file name parameter). Your rc.common file exhibits a correct way to include another script (precede with a dot (.)). Commented Aug 24, 2013 at 15:25
  • I checked all of my rc.d scripts in Fedora and none of them used the mechanism you're showing. Which distro of Linux do you have? Commented Aug 24, 2013 at 15:27
  • it's Openwrt, a lightweight distribution for wireless router with 32RAM or more. Commented Aug 25, 2013 at 0:54

1 Answer 1

2

From execve(2) on a fairly current Linux system:

Interpreter scripts

An interpreter script is a text file that has execute permission enabled and whose first line is of the form:

#! interpreter [optional-arg]

The interpreter must be a valid pathname for an executable which is not itself a script. If the filename argument of execve() specifies an interpreter script, then interpreter will be invoked with the following arguments:

interpreter [optional-arg] filename arg...

where arg... is the series of words pointed to by the argv argument of execve().

For portable use, optional-arg should either be absent, or be specified as a single word (i.e., it should not contain white space); [...]

I have not seen many scripts in the wild using the #!/bin/sh filename idiom. I find its use confusing.

Perhaps a simple test will illustrate. These files should be present in /tmp/test, which matters in this case, given the details of the interpreter line in test_interpreter.sh.

The script named in the "#!" line (rc_interpreter_line) arranges to run commands in the originally invoked script, which I do via the sourcing_script variable and shift command. The code you quote in the question does a rather more complicated version of this chaining. Without this kind of chaining, all that runs is the file named in the interpreter line.

Contents of rc_interpreter_line

echo '===='
echo $0 "$@"

TESTVAR=set

sourcing_script=$1
shift

. "$sourcing_script" "$@"

echo '===='

Contents of test_interpreter.sh

#!/bin/sh /tmp/test/rc_interpreter_line

echo '-----'
echo "My file name is test_interpreter.sh, but \$0 is $0"

echo Command line: $0 "$@"

echo "TESTVAR is '$TESTVAR'"
echo '-----'

exit 0

Permissions:

sh-4.2$ ls -l
total 8
-rw-r--r-- 1 dev dev 104 Aug 24 13:36 rc_interpreter_line
-rwxr-xr-x 1 dev dev 191 Aug 24 13:36 test_interpreter.sh

Sample runs. Run test_interpreter.sh directly first.

sh-4.2$ ./test_interpreter.sh -opt arg1 arg2
====
/tmp/test/rc_interpreter_line ./test_interpreter.sh -opt arg1 arg2
-----
My file name is test_interpreter.sh, but $0 is /tmp/test/rc_interpreter_line
Command line: /tmp/test/rc_interpreter_line -opt arg1 arg2
TESTVAR is 'set'
-----

The second invokes the shell more plainly. No execve(2) behavior firing, so this run of the shell simply runs the commands in test_interpreter.sh, treating that first line as a comment.

sh-4.2$ sh test_interpreter.sh -opt arg1 arg2
-----
My file name is test_interpreter.sh, but $0 is test_interpreter.sh
Command line: test_interpreter.sh -opt arg1 arg2
TESTVAR is ''
-----

But my personal preference is to avoid the idiom entirely. It's far clearer to me to simply use commands early in scripts like:

. /etc/rc.common

...rather than rely on "interesting" '#!' lines, and in doing so create different behavior when using ./my_script and sh my_script

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the detailed reply! but i still don't quite understand how the functions in the scripts are invoked by simply appending the function name to the full path of the startup scripts. for example, '/etc/init.d/vsftpd test' will execute a function named 'test' in either /etc/init.d/vsftpd or /etc/rc.common.Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.