11

I'm using this in a service declaration:

ExecStartPre=/usr/bin/docker pull "$DOCKER_USERNAME/redis-replication:latest"

In the log of systemd, I can see this when I try to start the service:

Usage: docker pull [OPTIONS] NAME[:TAG]
Pull an image or a repository from the registry
-a, --all-tags=false    Download all tagged images in the repository

It looks like systemd didn't execute the proper command but some weird one. What could it be and how to correct that?

Edited: Here's my entire unit file

[Unit]
Description=Run redis replication
After=docker.service
Requires=docker.service

[Service] Restart=always RestartSec=10s EnvironmentFile=/etc/vax/credentials EnvironmentFile=/etc/vax/centos-ip EnvironmentFile=/etc/vax/docker-auth EnvironmentFile=/etc/vax/cluster-prefix ExecStartPre=-/usr/bin/docker kill redisrep ExecStartPre=-/usr/bin/docker rm redisrep ExecStartPre=/usr/bin/docker pull "$DOCKER_USERNAME/redis-replication:latest" ExecStart=/usr/bin/docker run --rm --name redisrep -v /var/data/myproject/redis:/data -e S3_ACCESS_KEY=$S3_ACCESS_KEY -e S3_SECRET_KEY=$S3_SECRET_KEY -e S3_BUCKET=$S3_BUCKET -e BACKUP_PREFIX=$BACKUP_PREFIX -e REPLICATE_FROM_IP=$CENTOS_IP -e REPLICATE_FROM_PORT=6379 $DOCKER_USERNAME/redis-replication:latest ExecStop=/usr/bin/docker kill redisrep

[X-Fleet] MachineMetadata="machineIndex=1"

Phương Nguyễn
  • 321
  • 1
  • 3
  • 9
  • Post the entire unit file. – Michael Hampton Dec 21 '14 at 17:05
  • I don't think you should be using quotation marks there. – Michael Hampton Dec 22 '14 at 02:52
  • 1
    I removed the quotation marks and the error is still the same. Just notice that the error is resolved if I change $DOCKER_USERNAME to ${DOCKER_USERNAME} –  Dec 22 '14 at 03:17
  • The underscore is probably the issue, then. – Michael Hampton Dec 22 '14 at 03:20
  • Is there a basic guidelines on how to use environment variable in systemd service file? I'm on quite some trial and error so far. –  Dec 22 '14 at 03:24
  • @JennyD: The shell figures out very easily when the next character is a slash. – Pavel Šimerda Dec 23 '14 at 11:59
  • @JennyD I'm not getting into that sort of discussion on personal opinions on good and bad habits. I just say that it's the systemd parser that cannot cope with the syntax and that a shell would actually parse it as expected. Therefore your note appears to be (1) incorrect and (2) unrelated. – Pavel Šimerda Dec 23 '14 at 15:56

2 Answers2

10

After some research around, I found that it's ok to use quote in ExecStart definition of a systemd service file. As for using shell variable, it's necessary to use curly braces to clarify where the variable name end when non-space characters is connected to the variable itself.

In the above case, the system must have treat $DOCKER_USERNAME/redis as the variable name instead of $DOCKER_USERNAME. Add a curly braces then it is interpreted correctly.

Edit: More information about what syntax is ok with systemd can be found here: http://www.freedesktop.org/software/systemd/man/systemd.service.html#Command%20lines

Basically most shell notation is ok, with the except of pipe operators.

Phương Nguyễn
  • 321
  • 1
  • 3
  • 9
  • Shouldn't this be reported with systemd? They're apparently using shell-like variable syntax but they divert from the syntax slightly, which is very likely unintentional. – Pavel Šimerda Dec 23 '14 at 12:01
  • 3
    Also would you please add an example of the syntax accepted by systemd so the answer is clear to casual readers that find this answer by a web search? – Pavel Šimerda Dec 23 '14 at 15:58
  • Thanks, I added the link where a reader could read more on systemd service file syntax. Though this question is specifically about quote and dollarsign and I think my answer already cleared it. – Phương Nguyễn Dec 23 '14 at 16:53
5

I've just struggled to quote and run the following command line in the ExecStart key of a service unit file:

IFS=$'\n'; f=($(ls $HOME/bk.d/DuckieTV*.backup | tail -n +2)); echo "${f[@]}"

I'll quote it using systemd quoting rules, and then I'll explain what I learned in the process. It appears complicated because we expect it to follow the quoting rules of POSIX shells, but in fact it's even simpler. Here's the ExecStart directive, properly quoted

ExecStart=/bin/bash -c 'IFS=$$\'\\n\'; f=($$(ls ${HOME}/bk.d/DuckieTV*.backup | tail -n +2)); echo \"$${f[@]}\"'

or

ExecStart=/bin/bash -c "IFS=$$\'\\n\'; f=($$(ls ${HOME}/bk.d/DuckieTV*.backup | tail -n +2)); echo \"$${f[@]}\""

So, the quoting rules:

  1. Enclose the string to be treated as a single argument either in single quotes (' ') or double quotes (" "). They are treated the same, it doesn't matter which one you choose, as long as the closing quote is the same as the opening quote
  2. Scan the string from left to right, replacing ' with \', " with \" and $ with $$

If you want systemd to do variable substitution, then don't quote $, but systemd doesn't treat $var and ${var} as the same. They are both replaced by the environment value of var, but word splitting will be different in each case: with $var, after replacing the value of var, the words will be split on whitespace, whereas with ${var} there will be no word splitting. Effectively, systemd treats $var as a POSIX shell would do, but it treats ${var} as a POSIX shell would treat "$var".