How to set environment variable in pre-start in Upstart script?
Asked Answered
D

3

10

We have a custom C++ daemon application that forks once. So we've been doing this in our Upstart script on Ubuntu 12.04 and it works perfectly:

expect fork
exec /path/to/the/app

However now we need to pass in an argument to our app which contains the number of CPUs on the machine on which it runs:

cat /proc/cpuinfo | grep processor | wc -l

Our first attempt was this:

expect fork
exec /path/to/the/app -t `cat /proc/cpuinfo | grep processor | wc -l`

While that starts our app with the correct -t value, Upstart tracks the wrong pid value, I'm assuming because those cat, grep & wc commands all launch processes in exec before our app.

I also tried this, and even it doesn't work, I guess because setting an env var runs a process? Upstart still tracks the wrong pid:

expect fork
script
    NUM_CORES=32
    /path/to/the/app -t $NUM_CORES
end script

I've also tried doing this in an env stanza but apparently those don't run commands:

env num_cores=`cat /proc/cpuinfo | grep processor | wc -l`

Also tried doing this in pre-start, but env vars set there don't have any values in the exec stanza:

pre-start
    NUM_CORES=32
end script

Any idea how to get this NUM_CORES set properly, and still get Upstart to track the correct pid for our app that forks once?

Delectation answered 26/9, 2012 at 21:1 Comment(0)
W
17

It's awkward. The recommended method is to write an env file in the pre-start stanza and then source it in the script stanza. It's ridiculous, I know.

expect fork

pre-start script
    exec >"/tmp/$UPSTART_JOB"
    echo "NUM_CORES=$(cat /proc/cpuinfo | grep processor | wc -l)"
end script

script
    . "/tmp/$UPSTART_JOB"
    /path/to/app -t "$NUM_CORES"
end script

post-start script
    rm -f "/tmp/$UPSTART_JOB"
end script

I use the exec line in the pre-start because I usually have multiple env variables and I don't want to repeat the redirection code.

This only works because the '. ' command is a built-in in dash and thus no process is spawned.

Wale answered 29/9, 2012 at 3:23 Comment(3)
Super helpful! One thing: I think you still need to use "exec /path/to/app" inside the script block if that's what you needed for pid tracking before. Once I added "exec" back this worked beautifully for me.Concubine
@CodyA.Ray that's completely dependent on the forking behavior of the service being started. In our case, expect daemon may have been appropriate.Wale
Couldn't be more thankful for this!! Was struggling with it for over a week.Axis
S
2

According to zram-config's upstart config:

script
    NUM_CORES=$(grep -c ^processor /proc/cpuinfo | sed 's/^0$/1/')
    /path/to/the/app -t $NUM_CORES
end script
Sophistication answered 30/5, 2013 at 8:53 Comment(1)
The problem with that is that $() forks a child process (and then the pipe causes another fork), which can confuse the fork tracking. If you fork more than twice before your actual daemon, older upstart can't track the daemon process and then respawn won't work right. This works for zram-config because that only runs once and doesn't need respawn, but it's not universal. :) That's why @mpm's answer is necessary.Tremulant
D
0

I would add

export NUM_CORES

after assigning it a value in "script". I remember that a /bin/sh symlinked to a non-Bash shell may run scripts, so I would avoid Bash-only constructs.

Re: using the "env" stanza, it passes values literally and does not process them using shell conventions.

Dilate answered 19/2, 2013 at 18:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.