Systemd string escaping
Asked Answered
W

3

11

If I run this command

/bin/bash -c 'while true;do /usr/bin/etcdctl set my-container "{\"host\": \"1\", \"port\": $(/usr/bin/docker port my-container 5000 | cut -d":" -f2)}" --ttl 60;sleep 45;done'

I get back from etcd what I expect {"host":"1", "port":49155}

But if I put it in a systemd file

ExecStart=/bin/bash -c 'while true;do /usr/bin/etcdctl set my-container "{\"host\": \"1\", \"port\": $(/usr/bin/docker port my-container 5000 | cut -d":" -f2)}" --ttl 60;sleep 45;done'

I get back {host:1, port:49155}

Any idea of why the escaping is different inside of the file? How can I fix it? Thanks!!

Wellspring answered 29/5, 2014 at 14:21 Comment(0)
K
14
systemd-escape '\"a fun thing"\' 

output: \x5c\x22a\x20fun\x20thing\x22\x5c

[Service]
ExecStart=/bin/sh -c 'echo "\x5c\x22a\x20fun\x20thing\x22\x5c"'

will print a fun thing

Kassandra answered 14/7, 2016 at 7:2 Comment(1)
The OP's problem is with being able to emit literal quotes from echo. That is to say, they want to know how to print "a fun thing", not a fun thing.Mesothelium
T
7

Systemd is doing isn't like bash as you now know, hence the escaping problem. In fact, systemd removes single and double quotes after parsing them. That fact is right out of the documentation (I went thru this too, then read :D).

The solution, call a script that echo back that info you need (with escaped quotes) if your purpose allows that.

Tuscan answered 26/11, 2014 at 22:46 Comment(6)
I don't get it, please take a look at this unit file gist.github.com/digital-wonderland/… ... It's full of double qoutes and it couldn't even work without them, right?Brigette
This is untrue. I mean, yes, systemd does remove quotes, but it does so in the same sense in which the shell removes quotes (and yes, normal shell parsing does have a step called "quote removal"), in that it removes literal quotes after using them to guide string-splitting. ExecStart=/usr/bin/docker run something/else -- -c "a fun thing" is absolutely not identical in behavior to ExecStart=/usr/bin/docker run something/else -- -c a fun thingMesothelium
(Yes, it shows it in the logs in a way that doesn't represent the actual divisions between argv elements, but if you look at /proc/$pid/cmdline for the invoked service, you'll see NULs where the boundaries properly belong).Mesothelium
I'm not even sure when the edit by naftuli Kay happened but i don't think I added that part @CharlesDuffyTuscan
Oh, wow. That edit by @NaftuliKay really should have been a separate answer.Mesothelium
@Naftuli Kay what did you add to my answer?Tuscan
M
6

In short -- it's different because systemd does its own string-splitting, unescaping and expansion, and the logic it uses isn't POSIX-compliant.

You can still do what you want, but you'll need more backslashes:

ExecStart=/bin/bash -c '\
  while :; do \
    port=$(/usr/bin/docker port my-container 5000 | cut -d: -f2); \
    /usr/bin/etcdctl set my-container "{\\\"host\\\": \\\"1\\\", \\\"port\\\": $port}" --ttl 60; \
    sleep 45; \
  done'

Note the use of \\\" for every literal " character in the desired output.


By the way -- personally, I advise against trying to generate JSON through string concatenation -- it's prone to injection vulnerabilities (if someone could put content of their choice in the output of the docker port command, they could potentially insert other key/value pairs into your data by having , "evil": true be in the port variable). This class of issues is avoided by using jq:

ExecStart=/bin/bash -c '\
  while :; do \
    port=$(/usr/bin/docker port my-container 5000 | cut -d: -f2); \
    json=$(jq -nc \
      --arg host 1 \
      --arg port "$port" \
      '{} | .host=$host | .port=($port | tonumber)'); \
    /usr/bin/etcdctl set my-container "$json" --ttl 60; \
    sleep 45; \
  done'

As a happy side effect, the above avoids needing any literal double-quote characters (the only ones used are syntactic to the copy of sh), so we don't need any backslashes to be passed through from systemd to the shell.

Mesothelium answered 23/8, 2017 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.