How do I put a ; in rc.conf variable value?

I'm writing an rc.d script. I need to do this:
Bash:
pidfile=/var/run/php_fpm_exporter.pid
command="/usr/sbin/daemon"
procname="/usr/local/bin/php-fpm_exporter"
command_args="-f -T ${name} -p ${pidfile} \
    /usr/bin/env ${procname} \
    --phpfpm.scrape-uri ${php_fpm_exporter_scrape_uri} \
    ${php_fpm_exporter_args}"

where the scrape URI in rc.conf will be:
Code:
php_fpm_exporter_scrape_uri="unix:///var/run/php-fpm.sock;/status"

Notice the ";" there.

I have php-fpm listening on a unix socket and presenting stats on /status. Problem is, it's using ";" as delimiter between file name and URL path, which gets interpreted as end of command. How do I escape it? '' don't seem to work, \ doesn't seem to work either.
 
You will need to put the ${php_fpm_exporter_scrape_uri} content in quotes, or else it's going to be interpreted as a command separation.

Something like this should do it:
Code:
command_args="-f -T ${name} -p ${pidfile} \
    /usr/bin/env ${procname} \
    --phpfpm.scrape-uri '${php_fpm_exporter_scrape_uri}' \
    ${php_fpm_exporter_args}"
 
I wish it were that simple, but I already tried that:
Code:
-f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid /usr/bin/env /usr/local/bin/php-fpm_exporter --phpfpm.scrape-uri 'unix:///var/run/php-fpm.sock;/status'
Starting php_fpm_exporter.
Unmatched '"'.
eval: /status     ": not found
/usr/local/etc/rc.d/php_fpm_exporter: WARNING: failed to start php_fpm_exporter

The first line is just `echo $command_args`. Something somewhere gets interpreted, but I don't know enough about how rc works to figure it out. Honestly, I was hoping it was something simple and I could get a solution without having to dig into rc shell scripts.

Here's the full script, without comments:

Bash:
. /etc/rc.subr

name=php_fpm_exporter
rcvar=php_fpm_exporter_enable

load_rc_config $name

: ${php_fpm_exporter_enable:="NO"}
: ${php_fpm_exporter_user:="www"}
: ${php_fpm_exporter_group:="www"}
: ${php_fpm_exporter_args:=""}
: ${php_fpm_exporter_scrape_uri:="tcp://127.0.0.1:9000/status"}


pidfile=/var/run/php_fpm_exporter.pid
command="/usr/sbin/daemon"
procname="/usr/local/bin/php-fpm_exporter"
command_args="-f -T ${name} -p ${pidfile} \
    /usr/bin/env ${procname} \
    --phpfpm.scrape-uri '${php_fpm_exporter_scrape_uri}' \
    ${php_fpm_exporter_args}"

echo $command_args

start_precmd=php_fpm_exporter_startprecmd

php_fpm_exporter_startprecmd()
{
    if [ ! -e ${pidfile} ]; then
        install \
            -o ${php_fpm_exporter_user} \
            -g ${php_fpm_exporter_group} \
            /dev/null ${pidfile};
    fi
}

load_rc_config $name
run_rc_command "$1"
 
Not really sure where this comes from, it might be the /usr/local/bin/php-fpm_exporter code itself that's emitting it.
Thank you, but no, the exporter works as expected when executed manually on the command line. It's written in Go, that comes from one of the scripts.
 
Run sh -x /usr/local/etc/rc.d/php_fpm_exporter start. Then we'll have a better idea of where the error comes from.
 
Here's the relevant part:
Bash:
Starting php_fpm_exporter.
+ [ -n '' ]
+ _cd=''
+ _doit='/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter     --phpfpm.scrape-uri unix:///var/run/php-fpm.sock;/status     '
+ [ -n www ]
+ _doit=$'su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter     --phpfpm.scrape-uri unix:///var/run/php-fpm.sock;/status     "\''
+ [ -n '' ]
+ [ -n '' ]
+ _doit=$' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter     --phpfpm.scrape-uri unix:///var/run/php-fpm.sock;/status     "\''
+ _run_rc_doit $' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter     --phpfpm.scrape-uri unix:///var/run/php-fpm.sock;/status     "\''
+ debug $'run_rc_command: doit:  limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter     --phpfpm.scrape-uri unix:///var/run/php-fpm.sock;/status     "\''
+ eval $' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter     --phpfpm.scrape-uri unix:///var/run/php-fpm.sock;/status     "\''
+ limits -C daemon su -m www -c 'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter     --phpfpm.scrape-uri unix:///var/run/php-fpm.sock;/status     "'
sh: /status: not found
+ _return=127
Which all happens in /etc/rc.subr when calling "start"

The above is for this version, without single quotes around php_fpm_exporter_scrape_uri, because it's easier to see what's going on this way:
Bash:
command_args="-f -T ${name} -p ${pidfile} \
    /usr/bin/env ${procname} \
    --phpfpm.scrape-uri ${php_fpm_exporter_scrape_uri} \
    ${php_fpm_exporter_args}"
Adding single quotes screws up the eval.

The only way I found to escape ";" is to replace with the ASCII code, as in "\073". So, if I put in rc.conf:
Code:
php_fpm_exporter_scrape_uri="unix:///var/run/php-fpm.sock\073/status"
it gets it past rc.subr, but then the exporter doesn't understand the address and complains that there is no file called "/var/run/php-fpm.sock073/status". Which, yeah, can't argue with that.

Maybe I'll find something tomorrow, today it's too late.

Edit: oh, also, using \073 screws up the config manager (Saltstack) because it interprets \0 as end of string and complains that it doesn't like Null.
 
Ok, the problem is getting the quotes within quotes within quotes to be processed correctly. It's not the semicolon that's the problem, the argument to --phpfpm.scrape-uri unix:///var/run/php-fpm.sock;/status needs to be quoted correctly, either --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status" or --phpfpm.scrape-uri 'unix:///var/run/php-fpm.sock;/status'.

Another try:
Code:
command_args="-f -T ${name} -p ${pidfile} \
    /usr/bin/env ${procname} \
    --phpfpm.scrape-uri \'${php_fpm_exporter_scrape_uri}\' \
    ${php_fpm_exporter_args}"
Or
Code:
command_args="-f -T ${name} -p ${pidfile} \
    /usr/bin/env ${procname} \
    --phpfpm.scrape-uri \"${php_fpm_exporter_scrape_uri}\" \
    ${php_fpm_exporter_args}"
 
Nope, tried that.

Double quotes:
Bash:
Starting php_fpm_exporter.
+ [ -n '' ]
+ _cd=''
+ _doit='/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status"     '
+ [ -n www ]
+ _doit=$'su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status"     "\''
+ [ -n '' ]
+ [ -n '' ]
+ _doit=$' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status"     "\''
+ _run_rc_doit $' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status"     "\''
+ debug $'run_rc_command: doit:  limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status"     "\''
+ eval $' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status"     "\''
+ limits -C daemon su -m www -c 'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status"     "'
/status     : Command not found.
+ _return=1

Single quotes
Bash:
Starting php_fpm_exporter.
+ [ -n '' ]
+ _cd=''
+ _doit=$'/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri \\\'unix:///var/run/php-fpm.sock;/status\\\'     '
+ [ -n www ]
+ _doit=$'su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri \\\'unix:///var/run/php-fpm.sock;/status\\\'     "\''
+ [ -n '' ]
+ [ -n '' ]
+ _doit=$' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri \\\'unix:///var/run/php-fpm.sock;/status\\\'     "\''
+ _run_rc_doit $' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri \\\'unix:///var/run/php-fpm.sock;/status\\\'     "\''
+ debug $'run_rc_command: doit:  limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri \\\'unix:///var/run/php-fpm.sock;/status\\\'     "\''
+ eval $' limits -C daemon  su -m www -c \'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri \\\'unix:///var/run/php-fpm.sock;/status\\\'     "\''
eval: 1: Syntax error: Unterminated quoted string
[code]
 
Bash:
daemon su -m www -c 'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri "unix:///var/run/php-fpm.sock;/status"     "'

Basically you have a ' followed by " followed by "
What you should do is to remove the last ", and just escape the ; with \; ?
Bash:
daemon su -m www -c 'sh -c "/usr/sbin/daemon  -f -T php_fpm_exporter -p /var/run/php_fpm_exporter.pid     /usr/bin/env /usr/local/bin/php-fpm_exporter server     --phpfpm.scrape-uri unix:///var/run/php-fpm.sock\;/status     "'
 
This is not a direct solution to the underlying issue but rather a possible design change to workaround the issue without making things look patchy: maybe you could split the rc.conf variable php_fpm_exporter_scrape_uri in two variables: php_fpm_exporter_scrape_base_uri and an optional php_fpm_exporter_scrape_uri_endpoint.

The file rc.conf could thereby have:
php_fpm_exporter_scrape_base_uri="unix:///var/run/php-fpm.sock"
php_fpm_exporter_scrape_uri_endpoint="/status"

And you could then do the concatenation in your rc.d script.

This could be a last resort solution if you are running out of time.
 
What you should do is to remove the last ", and just escape the ; with \; ?
Problem is, I'm not the one adding those quotes and asking the user to work around weird escape problems smells really bad to me. Even if the user is me a few months from now.

maybe you could split the rc.conf variable php_fpm_exporter_scrape_uri in two variables: php_fpm_exporter_scrape_base_uri and an optional php_fpm_exporter_scrape_uri_endpoint.
That might have been a workaround, but I'd still have to inject ";" into the string that gets passed to start() function, I think I would have ended up in the same place. I saw this message too late though, I solved it in a different way.

In the end I overwrote the start function with my own by defining "start_cmd" and setting the start() function to be more straight-forward:
Bash:
limits -C daemon su -m www -c "${command} ${command_args}"
I also had to escape double quotes around the variable, but at least it worked this time.

This is the script I ended up with, if anyone is curious:
Bash:
#!/bin/sh

# PROVIDE: php_fpm_exporter
# REQUIRE: LOGIN
# KEYWORD: shutdown
#
# Add the following lines to /etc/rc.conf.local or /etc/rc.conf
# to enable this service:
#
# php_fpm_exporter_enable (bool):
#   Set to NO by default.
#   Set it to YES to enable php_fpm_exporter.
# php_fpm_exporter_user (string):
#   Set user that php_fpm_exporter will run under.
#   Default is "www".
# php_fpm_exporter_group (string):
#   Set group that php_fpm_exporter will run under.
#   Default is "www".
# php_fpm_exporter_args (string):
#   Set extra arguments to pass to php_fpm_exporter.
#   See https://github.com/hipages/php-fpm_exporter#usage for details.
#   Default is "".
# php_fpm_exporter_scrape_uri (string):
#   Set FastCGI address, e.g. "unix:///var/run/php-fpm.sock;/status".
#   Default is "tcp://127.0.0.1:9000/status".

. /etc/rc.subr

name=php_fpm_exporter
rcvar=php_fpm_exporter_enable
start_cmd="${name}_start"
start_precmd=php_fpm_exporter_startprecmd
pidfile="/var/run/${name}.pid"

load_rc_config $name

: ${php_fpm_exporter_enable:="NO"}
: ${php_fpm_exporter_user:="www"}
: ${php_fpm_exporter_group:="www"}
: ${php_fpm_exporter_args:=""}
: ${php_fpm_exporter_scrape_uri:="tcp://127.0.0.1:9000/status"}

command="/usr/sbin/daemon"
procname="/usr/local/bin/php-fpm_exporter"
command_args="-f -T ${name} -p ${pidfile} \
    /usr/bin/env ${procname} server \
    --phpfpm.scrape-uri \"${php_fpm_exporter_scrape_uri}\" \
    ${php_fpm_exporter_args}"

php_fpm_exporter_startprecmd()
{
    if [ ! -e ${pidfile} ]; then
        install \
            -o ${php_fpm_exporter_user} \
            -g ${php_fpm_exporter_group} \
            -m 0644 \
            /dev/null ${pidfile};
    fi
}

php_fpm_exporter_start()
{
    limits -C daemon su -m www -c "${command} ${command_args}"
}

run_rc_command "$1"

Question: should this be reported as a bug to FreeBSD base?
 
Back
Top