InitScripts
InitScripts are another useful AppJail feature that are the core of Makejail. An initscript is simply a sh(1)
script that AppJail loads to run a stage
. A stage
is a function within the initscript that is executed by an AppJail command as follows:
create
: Stage executed byappjail-start(1)
before the jail is started.start
: Stage executed byappjail-start(1)
after the jail is started.stop
: Stage executed byappjail-stop(1)
before the jail is stopped.cmd
: Stage executed byappjail-run(1)
.custom:<arbitrary string>
: Stage executed by appjail-run(1). The difference between cmd and this stage is that the latter can be defined using a valid arbitrary string. A valid arbitrary string is^[a-zA-Z0-9_][a-zA-Z0-9_-]*$
.apply
: Stage executed byappjail-apply(1)
.
Each stage
has pre-
and post-
functions. pre-
is executed before stage
is executed and if it fails, stage
will not be executed. post-
will be executed after pre-
and stage
even if they fail. pre-
and stage
affect the exit status, but post-
does not. stage
and functions in a initscript are all optional, AppJail will execute them if they exist.
The following example contains all stages and functions:
_parse_args()
{
local arg
for arg in "$@"; do
printf "<%s> " "${arg}"
done
echo
}
precreate()
{
echo -n "precreate args: "
_parse_args "$@"
}
create()
{
echo -n "create args: "
_parse_args "$@"
}
postcreate()
{
echo -n "postcreate args: "
_parse_args "$@"
}
prestart()
{
echo -n "prestart args: "
_parse_args "$@"
}
start()
{
echo -n "start args: "
_parse_args "$@"
}
poststart()
{
echo -n "poststart args: "
_parse_args "$@"
}
precmd()
{
echo -n "precmd args: "
_parse_args "$@"
}
cmd()
{
echo -n "cmd args: "
_parse_args "$@"
}
postcmd()
{
echo -n "postcmd args: "
_parse_args "$@"
}
prestop()
{
echo -n "prestop args: "
_parse_args "$@"
}
stop()
{
echo -n "stop args: "
_parse_args "$@"
}
poststop()
{
echo -n "poststop args: "
_parse_args "$@"
}
We use the initscript
option in appjail-quick(1)
to use this initscript in our new jail.
chmod +x /tmp/initscript
appjail quick myjail overwrite initscript=/tmp/initscript
The jail will not start because we are not using the start
option, this is because we will start the jail manually to pass arguments for create
and for start
stages.
# appjail start -c 'parameter1=I am the create parameter #1' -c 'parameter2=I am the create parameter #2' -s 'parameter1=I am the start parameter #1' -s 'parameter2=I am the start parameter #2' myjail
...
[00:00:17] [ debug ] [myjail] Running initscript `/usr/local/appjail/jails/myjail/init` ...
[00:00:17] [ debug ] [myjail] Running precreate() ...
precreate args: <--parameter1> <I am the create parameter #1> <--parameter2> <I am the create parameter #2>
[00:00:17] [ debug ] [myjail] precreate() exits with status code 0
create args: <--parameter1> <I am the create parameter #1> <--parameter2> <I am the create parameter #2>
[00:00:17] [ debug ] [myjail] create() exits with status code 0
postcreate args: <--parameter1> <I am the create parameter #1> <--parameter2> <I am the create parameter #2>
[00:00:17] [ debug ] [myjail] postcreate() exits with status code 0
[00:00:17] [ debug ] [myjail] `/usr/local/appjail/jails/myjail/init` exits with status code 0
[00:00:17] [ debug ] [myjail] Creating...
[00:00:19] [ info ] [myjail] myjail: created
[00:00:20] [ debug ] [myjail] Running initscript `/usr/local/appjail/jails/myjail/init` ...
[00:00:20] [ debug ] [myjail] Running prestart() ...
prestart args: <--parameter1> <I am the start parameter #1> <--parameter2> <I am the start parameter #2>
[00:00:20] [ debug ] [myjail] prestart() exits with status code 0
start args: <--parameter1> <I am the start parameter #1> <--parameter2> <I am the start parameter #2>
[00:00:20] [ debug ] [myjail] start() exits with status code 0
poststart args: <--parameter1> <I am the start parameter #1> <--parameter2> <I am the start parameter #2>
[00:00:20] [ debug ] [myjail] poststart() exits with status code 0
[00:00:20] [ debug ] [myjail] `/usr/local/appjail/jails/myjail/init` exits with status code 0
As you can see, the pre-
and post-
functions receive the same parameters as their stage
.
appjail-run(1)
will run cmd
whenever we want and only if the jail is running.
# appjail run -p 'msg=Hello, world!' myjail
[00:00:01] [ debug ] [myjail] Running initscript `/usr/local/appjail/jails/myjail/init` ...
[00:00:01] [ debug ] [myjail] Running precmd() ...
precmd args: <--msg> <Hello, world!>
[00:00:01] [ debug ] [myjail] precmd() exits with status code 0
cmd args: <--msg> <Hello, world!>
[00:00:01] [ debug ] [myjail] cmd() exits with status code 0
postcmd args: <--msg> <Hello, world!>
[00:00:01] [ debug ] [myjail] postcmd() exits with status code 0
[00:00:01] [ debug ] [myjail] `/usr/local/appjail/jails/myjail/init` exits with status code 0
Finally, we can pass arguments to stop
:
# appjail stop -p 'msg=Bye ...' myjail
[00:00:02] [ debug ] [myjail] Running initscript `/usr/local/appjail/jails/myjail/init` ...
[00:00:02] [ debug ] [myjail] Running prestop() ...
prestop args: <--msg> <Bye ...>
[00:00:02] [ debug ] [myjail] prestop() exits with status code 0
stop args: <--msg> <Bye ...>
[00:00:02] [ debug ] [myjail] stop() exits with status code 0
poststop args: <--msg> <Bye ...>
[00:00:02] [ debug ] [myjail] poststop() exits with status code 0
[00:00:02] [ debug ] [myjail] `/usr/local/appjail/jails/myjail/init` exits with status code 0
...
appjail-enable(1)
is a command to enable arguments that need to be passed when the user does not provide them. This is necessary for commands such as appjail-startup(1)
or appjail-restart(1)
because these commands does not accept arguments for stages.
appjail enable myjail start -c 'create_msg=Hi everyone!' -s 'start_msg=Welcome.'
appjail enable myjail stop -p 'stop_msg=Bye.'
If we start or stop the jail without passing arguments, appjail-start(1)
or appjail-stop(1)
will use the arguments of the appjail-enable(1)
command.
# appjail start myjail
...
[00:00:18] [ debug ] [myjail] Running initscript `/usr/local/appjail/jails/myjail/init` ...
[00:00:18] [ debug ] [myjail] Running precreate() ...
precreate args: <--create_msg> <Hi everyone!>
[00:00:18] [ debug ] [myjail] precreate() exits with status code 0
create args: <--create_msg> <Hi everyone!>
[00:00:18] [ debug ] [myjail] create() exits with status code 0
postcreate args: <--create_msg> <Hi everyone!>
[00:00:18] [ debug ] [myjail] postcreate() exits with status code 0
[00:00:18] [ debug ] [myjail] `/usr/local/appjail/jails/myjail/init` exits with status code 0
[00:00:18] [ debug ] [myjail] Creating...
[00:00:18] [ info ] [myjail] myjail: created
[00:00:19] [ debug ] [myjail] Running initscript `/usr/local/appjail/jails/myjail/init` ...
[00:00:19] [ debug ] [myjail] Running prestart() ...
prestart args: <--start_msg> <Welcome.>
[00:00:19] [ debug ] [myjail] prestart() exits with status code 0
start args: <--start_msg> <Welcome.>
[00:00:19] [ debug ] [myjail] start() exits with status code 0
poststart args: <--start_msg> <Welcome.>
[00:00:19] [ debug ] [myjail] poststart() exits with status code 0
[00:00:19] [ debug ] [myjail] `/usr/local/appjail/jails/myjail/init` exits with status code 0
InitScripts are executed in the host, not in the jail. This decision is to run tasks in the host and tasks in the jail. To execute commands in a jail we use jexec(8)
, but if we use fixed strings carelessly we may have some problems.
One problem we can see is that we rename a jail. The problem is that a command such as jexec(8)
that uses the name of the jail to execute commands on it, will not execute correctly because the jail does not exist. Worse, there is a possibility that commands will be executed on a jail with totally different name than the one we intended.
AppJail solves this problem by keeping things simple: using the following environment variables:
APPJAIL_CONFIG
: AppJail configuration file.APPJAIL_JAILDIR
: Jail directory ({APPJAIL_ROOTDIR}/jail
)APPJAIL_JAILNAME
: Jail name.APPJAIL_ROOTDIR
: Root directory of the jail ({JAILDIR}/{jail_name}
).APPJAIL_SCRIPT
: AppJail script.
With the above information we can make an initscript to display Hello, world!
:
cmd()
{
jexec -l "${APPJAIL_JAILNAME}" sh -c 'echo "Hello, world!"'
}
# appjail quick myjail initscript=/tmp/initscript start overwrite
# appjail run myjail
[00:00:06] [ debug ] [myjail] Running initscript `/usr/local/appjail/jails/myjail/init` ...
Hello, world!
[00:00:06] [ debug ] [myjail] cmd() exits with status code 0
[00:00:07] [ debug ] [myjail] `/usr/local/appjail/jails/myjail/init` exits with status code 0
As mentioned above, you can use a custom stage. This is very useful when many Makejails are included so as not to overlap stages.
custom:python()
{
"${APPJAIL_SCRIPT}" cmd jexec "${APPJAIL_JAILNAME}" python3.9
}
custom:php()
{
"${APPJAIL_SCRIPT}" cmd jexec "${APPJAIL_JAILNAME}" php -a
}
custom:top()
{
"${APPJAIL_SCRIPT}" cmd jexec "${APPJAIL_JAILNAME}" top -a
}
The above initscript has three custom stages: python
, php
and top
. To run any of them, use appjail-run(1)
with the -s
parameter.
# python
appjail run -s python pyapp
# php
appjail run -s php pyapp
# top
appjail run -s top pyapp
InitScripts are a bit complex and there are some ways to create them much easier (See Makejails).