Scripts

Updated: April 19, 2023

The second section of the buildfile starts with the [+script] attribute. This attribute informs mkifs that the specified file is a script file, a sequence of commands that you want the procnto process manager to execute when it has completed its own startup.

The script file can be external or inline, and its name is arbitrary, though QNX usually uses some variation on .script. Multiple scripts are concatenated into one and are interpreted in the order in which they appear.

Script file syntax

Script files look just like regular shell scripts, but have the following differences:

In this example, the script file is an inline file called .script, which contains the following:

sched_aps IOPKT 20 5

procmgr_symlink ../../proc/boot/libc.so /usr/lib/ldqnx.so.2

[pri=12] devc-ser8250-abc123 -F -e -c14745600  -b115200 0xc8000000 ^2,15 &

reopen 5.0 /dev/ser1
    
display_msg Serial Driver Started

If we review this file, we find that the script:

  1. Creates an adaptive partition named 'IOPKT' with a budget of 20% and a critical budget of 5 ms during system startup.
  2. Creates a symbolic link in the process manager's in-memory prefix tree.
  3. Starts a serial driver (the fictional devc-ser8250-abc123) in edited mode with hardware flow control disabled at a baud rate of 115200 bps at a particular physical memory address.
  4. Issues a reopen command to redirect standard input, output, and error.
  5. Displays a message.

As mentioned in Environment variables, the bootstrap file can set the _CS_PATH and _CS_LIBPATH configuration strings. You can set PATH, LD_LIBRARY_PATH, and other environment variables if the programs in your script need them.

Internal commands

Internal commands are commands that mkifs recognizes and are not loaded from the host’s filesystem. These are:
display_msg [message]
Output the given text. This command takes the message to be displayed as an argument.
procmgr_symlink <dst_path> <link_path>
The equivalent of ln -P (create the link in the process manager's in-memory prefix tree), except that ln doesn't need to be present.
reopen [[ timeout ] device_path ]
Sets the default of the file descriptors STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO to the given device path. Otherwise, all processes created by the script are created with stdin, stdout, and stderr (and their associated file descriptors) pointing to /dev/text.
Note: Writing to /dev/text will cause the kernel to output the data to its debug device (typically unbuffered serial port output). Since only one core can be in the kernel, all kernel operations must wait until the data is transferred out to that debug device. Due to the slow speeds of serial ports, this will add significant latency.
The command changes the STD*_FILENO file descriptors for all subsequently created processes to use the provided device_path. The timeout value specifies the maximum number of seconds to wait for the file to appear. It can include one decimal digit to specify tenths of a second. The default is 5.0 seconds.
sched_aps partition_name [ budget [ critical ]]
Create a partition with the given name in the startup script (before launching any commands in the partition), and if specified, with the given default budget and default critical budget. For these last two arguments, you can include the symbols of % and ms for the units if you so choose. Note that you can't include a decimal place (e.g. 5.0) in the numeric arguments. The default budget is 0% and the default critical budget is 0 ms.
waitfor [timeout] pathname
Wait until a stat() on the given pathname succeeds. The timeout can include one decimal digit to specify tenths of a second. The default is 5.0 seconds.

Script processing

Buildfile scripts support foreground and background processes. Just as in the shell, you can specify an ampersand (&) on the command line to make the program run in the background. If you run a program in the foreground and it doesn't exit, then the rest of the script is never executed, and the system might not become fully operational.

For a command to be run, its executable must be available when the script is executed. You can add the executable to the image, or get it from a filesystem that's started before the executable is required. Placing the executable in a filesystem reduces the size of the OS image.

Binding processes to a CPU core ([cpu=] attribute)

You can use the [cpu=] attribute to instruct the procnto module to bind processes to a specific CPU core. The syntax for this attribute is like that of any “value” attribute: [attribute=value] filename. Use this attribute in one of the following ways:

Bind the named program to the specified core
[cpu=zero-based core number] program name
Bind all programs launched after this point to the specified core
[cpu=zero-based core number]
Allow the named program to run on any CPU core
[cpu=*] program name

At boot time, if there is no processor core with the specified index, procnto displays a warning message and launches the command with no runmask restrictions.

Note: Due to a limitation in the boot image records, this syntax allows only the specification of a single CPU and not a more generic runmask. Use the on utility to spawn a process within a fully specified runmask.

The script file on the target

The script file stored on the target isn't the same as the original specification of the script file in the buildfile. This difference is because the script file on the target is not the original ASCII text: mkifs parses the text commands in the script file and stores only the parsed output on the target.

This design minimizes the work that the process manager has to do at runtime when it starts up and processes the script file — we didn't want to have to include a complete shell interpreter in the process manager!