Passing variables to use in the preseed file for a Debian Jessie installation
Is it possible to add a variable via the boot prompt to the Debian installer, so that variable can be used in a preseed file?
In particular, I'm trying to solve the following problem:
We have a pretty extensive post-install script that is generally downloaded from a server. But now I want to create Packer images and keep the post-install script in version control together with the other Packer files. For accessing the preseed, I can do "preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg" in the boot command. But now I want the installer to download the post-install script from that same location.
Currently, the post-install hook looks like this:
d-i preseed/late_command string wget -q -O /tmp/postinstall.sh http://our.public.server/postinstall.jessie.sh ; sh /tmp/postinstall.sh
Ideally, I'd like to do something like:
d-i preseed/late_command string wget -q -O /tmp/postinstall.sh http://{{ .HTTPIP }}:{{ .HTTPPort }}/postinstall.jessie.sh ; sh /tmp/postinstall.sh
But of course the Debian installer will not replace those with the required values. So I was thinking that it might be possible to pass environment variable-like variables to the installer that we can use in the preseed file.
Any hints or tips are appreciated!
EDIT: Tried adding the late_command to the boot command, but that didn't get picked up.
EDIT: Tried preseed/run, but it runs in a different environment which does not allow the in-target command.
EDIT: This can be a workaround: How do I pipe commands together in a debian preseed file? But I'd prefer to have the script in a separate file. If it's not possible, it's not possible, though.
It depends on what OS you are using, but the Linux kernel will let you specify environment variables as kernel parameters. The Linux kernel documentation has some good information around it (important paragraph bolded):
The argument list
The kernel command line is parsed into a list of strings (boot arguments) separated by spaces. Most of the boot arguments have the form:
name[=value_1][,value_2]...[,value_10]
where 'name' is a unique keyword that is used to identify what part of the kernel the associated values (if any) are to be given to. Note the limit of 10 is real, as the present code handles only 10 comma separated parameters per keyword. (However, you can reuse the same keyword with up to an additional 10 parameters in unusually complicated situations, > assuming the setup function supports it.)
Most of the sorting is coded in the kernel source file init/main.c. First, the kernel checks to see if the argument is any of the special arguments 'root=', 'nfsroot=', 'nfsaddrs=', 'ro', 'rw', 'debug' or 'init'. The meaning of these special arguments is described below.
Then it walks a list of setup functions to see if the specified argument string (such as 'foo') has been associated with a setup function ('foo_setup()') for a particular device or part of the kernel. If you passed the kernel the line foo=3,4,5,6 then the kernel would search the bootsetups array to see if 'foo' was registered. If it was, then it would call the setup function associated with 'foo' (foo_setup()) and hand it the arguments 3, 4, 5, and 6 as given on the kernel command line.
Anything of the form 'foo=bar' that is not accepted as a setup function as described above is then interpreted as an environment variable to be set.
A (useless?) example would be to use 'TERM=vt100' as a boot argument.Any remaining arguments that were not picked up by the kernel and were not interpreted as environment variables are then passed onto PID 1, which is usually the init(1) program. The most common argument that is passed to the init process is the word 'single' which instructs it to boot the computer in single user mode, and not launch all the usual daemons. Check the manual page for the version of init(1) installed on your system to see what arguments it accepts.
Here is my boot_command
section in my virtualbox-iso
builder (for Ubuntu 18.04):
boot_command:
- '<esc><esc><enter><wait>'
- '/install/vmlinuz noapic fb=false '
- 'auto=true '
- 'hostname={{.Name}} '
- 'url=http://{{.HTTPIP}}:{{.HTTPPort}}/ubuntu.seed '
- 'initrd=/install/initrd.gz '
- 'http_proxy={{user `http_proxy`}} '
- 'packer_host={{.HTTPIP}} '
- 'packer_port={{.HTTPPort}} '
- 'hello=world '
- 'quiet --- <enter>'
The http_proxy
, packer_host
, packer_port
, and hello
parameters are completely optional, and will be converted into environment variables by the kernel.
In my ubuntu.seed
file, I have the following line to print out the hello
environment variable into a file:
d-i preseed/late_command string echo $hello > /target/home/packer/hello
When I import and start up the OVA, that file will be in my home directory with world
as its contents.
Ok, I solved it myself (with some help from @lieter_). Not too proud of it, but it works:
d-i preseed/late_command string wget -q -O /tmp/postinstall.sh http://`cat /proc/cmdline | sed 's/.*url=\([^ ]\+\).*/\1/'`/d-i/jessie/postinstall.sh ; sh /tmp/postinstall.sh
This does what I need, since we always add a url= to our command line when installing.
While looking to solve much the same issue I learned about some magic that is baked into the debian-installer
. You can use preseed_fetch
to pull from the URL if you add a /./
at the point in the URL that you want to set as the rootpath
for other commands to perform their relative fetches against.
Given a server with a preseed folder and a scripts folder with other files you want to use, if you pass url=http://{{.HTTPIP}}:{{.HTTPPort}}/http/./preseed/ubuntu.seed
you can then reference other files relative to the root path like preseed_fetch /scripts/somescript.sh /tmp/somescript.sh
.
d-i preseed/late_command string preseed_fetch /scripts/late_script /tmp/late_script; \
log-output -t late_script sh /tmp/late_script
More information can be found at https://hands.com/d-i/lenny/start.cfg and https://hands.com/d-i/ under the heading "url=magic".