1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
Dismiss Notice

Welcome To SNBForums

SNBForums is a community for anyone who wants to learn about or discuss the latest in wireless routers, network storage and the ins and outs of building and maintaining a small network.

If you'd like to post a question, simply register and have at it!

While you're at it, please check out SmallNetBuilder for product reviews and our famous Router Charts, Ranker and plenty more!

Extend "service" to Entware services

Discussion in 'Asuswrt-Merlin' started by cmkelley, Feb 11, 2019.

  1. cmkelley

    cmkelley Very Senior Member

    Joined:
    Aug 11, 2015
    Messages:
    557
    Location:
    Greater Los Angeles Area, California, USizicstania
    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: Feb 11, 2019
    thelonelycoder and L&LD like this.
  2. Dabombber

    Dabombber Occasional Visitor

    Joined:
    Apr 29, 2016
    Messages:
    48
    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: Feb 11, 2019
    L&LD and ColinTaylor like this.
  3. ColinTaylor

    ColinTaylor Part of the Furniture

    Joined:
    Mar 31, 2014
    Messages:
    8,015
    Location:
    UK
    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: Feb 11, 2019
    L&LD likes this.
  4. cmkelley

    cmkelley Very Senior Member

    Joined:
    Aug 11, 2015
    Messages:
    557
    Location:
    Greater Los Angeles Area, California, USizicstania
    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).
     
    L&LD likes this.
  5. Dabombber

    Dabombber Occasional Visitor

    Joined:
    Apr 29, 2016
    Messages:
    48
    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.
    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 "[email protected]"
        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
    
     
    Cam and L&LD like this.