What's new

Extend "service" to Entware services

  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

cmkelley

Very Senior Member
I got tired of typing '/opt/etc/init/S01syslog-ng restart', particularly when there's a perfectly good "service action_target" mechanism for the firmware services. So, not knowing any better, I set out to remedy that situation (it's late, I'm punchy, bear with me). In the spirit of the "add one line" mantra, it requires only a single line to be added to /jffs/scripts/service-event:
Code:
/jffs/scripts/entware-service "$1" "$2" & # service ACTION_TARGET for Entware
If you don't already have a /jffs/scripts/service-event script, create one:
Code:
#!/bin/sh

/jffs/scripts/entware-service "$1" "$2" & # service ACTION_TARGET for Entware
and make it executable
Code:
chmod +x /jffs/scripts/service-event
Create a /jffs/scripts/entware-service file (sorry, no fancy installer):
Code:
#!/bin/sh
#tof

# allow f/w "service" command to work on Entware services
# - services must be defined as /opt/etc/init.d/S##service_name
# - S##service_name must be an executable shell script

ACTION=$1
TARGET=$2

if [ $# -lt 2 ] || [ ! -x /opt/bin/find ]
then
    exit 1
fi

SRVNAME="$(/opt/bin/find /opt/etc/init.d/ -perm '-u+x' -name "S[0-9][0-9]$TARGET")"

if [ "X$SRVNAME" != "X" ]
then
    $SRVNAME $ACTION
fi

#eof
and make it executable
Code:
chmod +x /jffs/scripts/entware-service
Now you can restart Entware services with eg. "service restart_syslog-ng", "service restart_stubby", etc.

Annoyingly enough, just like when restarting a firmware service, you have to look in /tmp/syslog.log to see if it worked or not. Seriously, you can type "service tango_uniform" and it will reply with "Done."

EDIT: added -x test for /opt/bin/find as discussed in comments
 
Last edited:
If you want one line, why not just have in /jffs/scripts/service-event
Code:
#!/bin/sh

# service ACTION_TARGET for Entware
[ -x /opt/bin/find ] && /opt/bin/find /opt/etc/init.d/ -perm '-u+x' -name "S[0-9][0-9]$2" -execdir {} "$1" \; &

EDIT: Added [ -x /opt/bin/find ] check
 
Last edited:
You might want to put a check in for the existence of /opt/bin/find and exit the script if not found.

During boot there are system services which start before the Entware partition has mounted (dnsmasq, diskmon, firewall, upnp, vpnserver1, etc.). This means that the find command will not be found. TBH it's not really a problem because the script will just fall through to the bottom, but it's not very aesthetic.

Code:
+ ACTION=restart
+ TARGET=dnsmasq
+ [ 2 -lt 2 ]
+ /opt/bin/find /opt/etc/init.d/ -perm -u+x -name S[0-9][0-9]dnsmasq
/jffs/scripts/entware-service: line 19: /opt/bin/find: not found
+ SRVNAME=
+ [ X != X ]
 
Last edited:
If you want one line, why not just have in /jffs/scripts/service-event
Code:
#!/bin/sh

# service ACTION_TARGET for Entware
[ -x /opt/bin/find ] && /opt/bin/find /opt/etc/init.d/ -perm '-u+x' -name "S[0-9][0-9]$2" -execdir {} "$1" \; &

EDIT: Added [ -x /opt/bin/find ] check
2 reasons: 1st, I'm not anywhere near that good with scripting, the line I had was copied off rc.unslung with minimal changes; and 2nd, I thought it was going to be more complicated than it was when I started, I thought I'd have to use some sort of loop, so I started off with it in another script and never thought about if I could make it one line (not that I would have known how even if I had thought of it).
 
I got a bit annoyed with not seeing any output from this, so I moved the interception from service-event to a function in profile.add. Probably better this way since it only works from SSH. I also added two actions for dnsmasq; 'reconfigure' to match rc.func, and 'reload' because I will never remember reconfigure.
When it receives a SIGHUP, dnsmasq clears its cache and then re-loads _etc_hosts and _etc_ethers and any file given by --dhcp-hostsfile, --dhcp-hostsdir, --dhcp-optsfile, --dhcp-optsdir, --addn-hosts or --hostsdir. The dhcp lease change script is called for all existing DHCP leases. If --no-poll is set SIGHUP also re-reads _etc_resolv.conf. SIGHUP does NOT re-read the configuration file.
Underscores instead of forward slashes because cloudflare.

profile.add doesn't need a shebang, but it'll just be treated as a comment if you want one.
Code:
service() {
    if [ $# -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
        echo "Usage: service [ACTION_]SCRIPT [ARGUMENTS][; ...]"
        return
    fi

    local PASS=""
    if [ -x "/opt/bin/find" ]; then
        local CMDLIST
        local LINE
        if ! CMDLIST="$(. /jffs/scripts/string.sh; str_token "$*")"; then return 1; fi
        while read -r LINE
        do
            if [ "$LINE" = "reload_dnsmasq" ] || [ "$LINE" = "reconfigure_dnsmasq" ]; then
                echo "Sending SIGHUP to dnsmasq..."
                killall -HUP dnsmasq
                continue
            fi

            local SCRIPT="${LINE%% *}"
            local ARGS="${LINE#* }"
            local ACTION="${SCRIPT%%_*}"

            [ "$SCRIPT" = "$ARGS" ] && ARGS=""
            SCRIPT="${SCRIPT#*_}"
            [ "$ACTION" = "$SCRIPT" ] && ACTION=""

            if [ -z "$ACTION" ] || [ -z "$(/opt/bin/find /opt/etc/init.d/ -perm '-u+x' -name "S[0-9][0-9]$SCRIPT" -printf '.' -execdir sh -c "\"\$1\" \"$ACTION\" > /proc/$$/fd/1" _ "{}" \;)" ]; then
                PASS="$PASS;$LINE"
            fi
        done <<- EOF
            $CMDLIST
        EOF
    else
        /sbin/service "$@"
    fi

    if [ -n "$PASS" ]; then
        /sbin/service "${PASS#;}"
    fi
}

If you want to change the location of this, be sure to edit the include after CMDLIST= in profile.add
Code:
#!/bin/sh

# Variable containing a new line as "$()" strips it
_NL='
'

# Split a string ($1, or stdin if empty) by a delimiter ($2, default ;) and applies a seperator ($3, default \n) preserving quoted strings
# Usage: str_token [STRING] [DELIMITER] [SEPERATOR]
str_token() {
    local STRING="${1:-"$(cat)"}"
    local DELIMITER="${2:-";"}"
    local SEPERATOR="${3:-"$_NL"}"

    while [ -n "$STRING" ]; do
        local STRING_RIGHT="$STRING"
        while :; do
            STRING_QQ="${STRING_RIGHT%%"\""*}"
            STRING_RIGHT="${STRING_RIGHT#"$STRING_QQ"}"
            if [ -z "$STRING_RIGHT" ] || [ "${STRING_QQ%"\\"}" = "$STRING_QQ" ]; then break; fi
            STRING_RIGHT="${STRING_RIGHT#"\""}"
        done
        local STRING_QQ="${STRING%"$STRING_RIGHT"}"

        local STRING_Q="${STRING%%"'"*}"

        if [ "$STRING_QQ" != "$STRING" ] && [ ${#STRING_QQ} -lt ${#STRING_Q} ]; then
            printf "%s" "${STRING_QQ//"$DELIMITER"/"$SEPERATOR"}"

            STRING="${STRING#"$STRING_QQ\""}"

            STRING_RIGHT="$STRING"
            while :; do
                STRING_QQ="${STRING_RIGHT%%"\""*}"
                STRING_RIGHT="${STRING_RIGHT#"$STRING_QQ"}"
                if [ -z "$STRING_RIGHT" ] || [ "${STRING_QQ%"\\"}" = "$STRING_QQ" ]; then break; fi
                STRING_RIGHT="${STRING_RIGHT#"\""}"
            done
            STRING_QQ="${STRING%"$STRING_RIGHT"}"

            [ "$STRING_QQ" = "$STRING" ] && echo "Error: Unmatched quote" >&2 && return 1
            printf "\"%s\"" "$STRING_QQ"

            STRING="${STRING#"$STRING_QQ\""}"
        elif [ "$STRING_Q" != "$STRING" ]; then
            printf "%s" "${STRING_Q//"$DELIMITER"/"$SEPERATOR"}"

            STRING="${STRING#"$STRING_Q'"}"
            STRING_Q="${STRING%%"'"*}"
            [ "$STRING_Q" = "$STRING" ] && echo "Error: Unmatched quote" >&2 && return 1
            printf "'%s'" "$STRING_Q"

            STRING="${STRING#"$STRING_Q'"}"
        else
            printf "%s" "${STRING//"$DELIMITER"/"$SEPERATOR"}"
            STRING=""
        fi
    done
    printf "\n"
}

Example outputs made by echo'ing the commands instead of executing them (extra newlines for readability, ./ entries are cd'd to /opt/etc/init.d):
Code:
$ service
Usage: service [ACTION_]SCRIPT [ARGUMENTS][; ...]

$ service restart_nginx
./S80nginx restart

$ service restart_httpd
/sbin/service restart_httpd

$ service stop_*
./S80nginx stop
./S01syslog-ng stop
./S00timezone stop
./S80pixelserv-tls stop

$ service "restart_nginx;reload_dnsmasq;restart_httpd;stop_pixelserv-tls;sh \"service 'stop_ftpd;stop_samba'\";start_timezone;restart_nasapps"
./S80nginx restart
Sending SIGHUP to dnsmasq...
./S80pixelserv-tls stop
./S00timezone start
/sbin/service restart_httpd;sh "service 'stop_ftpd;stop_samba'";restart_nasapps

$ service "restart_httpd;sh 'echo hello;stop_ftpd"
Error: Unmatched quote

$ service "restart_httpd;sh \"echo \\\";stop_nginx;\\\"\";stop_ftpd"
/sbin/service restart_httpd;sh "echo \";stop_nginx;\"";stop_ftpd
 

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top