pibootctl

pibootctl is a utility for querying and manipulating the boot configuration of a Raspberry Pi. It is a relatively low level utility, and not intended to be as friendly (or as widely applicable) as raspi-config. It provides a command line interface only, but does attempt to be useful as a basis for more advanced interfaces (by providing input and output in human-readable, shell-interpretable, JSON, or YAML formats) as well as being useful in its own right.

The design philosophy of the utility is as follows:

  1. Be safe: the utility manipulates the boot configuration and it’s entirely possible to create a non-booting system as a result. To that end, if no backup of the current boot configuration exists, always take one before manipulating it.
  2. Be accessible: the Pi’s boot configuration lives on a FAT partition and is a simple ASCII text file. This means it can be read and manipulated by almost any platform (Windows, Mac OS, etc). Any backups of the configuration should be as accessible. To that end we use simple PKZIP files to store backups of boot configurations (in their original format), and place them on the same FAT partition as the configuration.
  3. Be extensible: Almost all commands should default to human readable input and output, but options should be provided for I/O in JSON, YAML, and a shell-parseable format.

Contents

Installation

If your distribution provides pibootctl then you should either find the utility is installed by default, or it should be installable via your package manager. For example:

$ sudo apt install pibootctl

It is strongly recommended to use a provided package rather than installing from PyPI as this will include configuration specific to your distribution. The utility can be removed via the usual mechanism for your package manager. For instance:

$ sudo apt purge pibootctl

Configuration

pibootctl looks for its configuration in three locations:

  1. /lib/pibootctl/pibootctl.conf
  2. /etc/pibootctl.conf
  3. ~/.config/pibootctl.conf

The last location is only intended for use by people developing pibootctl; for the vast majority of users the configuration should be provided by their distribution in one of the first two locations.

The configuration file is a straight-forward INI-style containing a single section titled “defaults”. A typical configuration file might look like this:

pibootctl.conf
[defaults]
boot_path = /boot
store_path = pibootctl
package_name = pibootctl
comment_lines = on
backup = on

The configuration specifies several settings, but the most important are:

boot_path
The mount-point of the boot partition (defaults to /boot).
store_path
The path under which to store saved boot configurations, relative to boot_path (defaults to pibootctl).
config_root
The “root” configuration file which is read first, relative to boot_path (defaults to config.txt). This is also the primary file that gets re-written when settings are changed.
mutable_files
The set of files within a configuration that may be modified by the utility (defaults to config.txt). List multiple files on separate lines. Currently, this must include config.txt.
comment_lines

If this is on, when lines in configuration files are no longer required, they will be commented out with a “#” prefix instead of being deleted. Defaults to off.

Note that, regardless of this setting, the utility will always search for commented lines to uncomment before writing new ones.

reboot_required
The file which should be created in the event that the active boot configuration is changed.
reboot_required_pkgs
The file to which the value of package_name should be appended in the event that the active boot configuration is changed.
package_name
The name of the package which contains the utility. Used by reboot_required_pkgs.
backup
If this is on (the default), any attempt to change the active boot configuration will automatically create a backup of that configuration if one does not already exist.

Line comments can be included in the configuration file with a # prefix. Another example configuration, typical for Ubuntu on the Raspberry Pi, is shown below:

pibootctl.conf
[defaults]
boot_path = /boot
store_path = pibootctl
mutable_files =
  config.txt
  syscfg.txt

reboot_required = /var/run/reboot-required
reboot_required_pkgs = /var/run/reboot-required.pkgs
package_name = pibootctl
backup = on

User Manual

The pibootctl utility defines several commands which can be used to query and manipulate the boot configuration of the Raspberry Pi:

diff
Display the differences between the specified boot configuration and the current one, or another specified configuration.
get
Retrieve the value of specified setting(s).
help
The default command, which describes the specified command or configuration setting.
list
List the stored boot configurations.
load
Restore the named boot configuration to be used at the next boot.
remove
Delete the specified boot configuration.
rename
Rename the specified boot configuration.
save
Save the current boot configuration to the specified name.
set
Modify or reset the specified configuration setting(s).
show
Show the specified stored configuration.
status
Output the current boot configuration; by default this only prints modified settings.

Typically, the status command is the first used, to determine the current boot configuration:

$ pibootctl status
+------------------------+-------+
| Name                   | Value |
|------------------------+-------|
| i2c.enabled            | on    |
| spi.enabled            | on    |
| video.overscan.enabled | off   |
+------------------------+-------+

After which the save command might be used to take a backup of the configuration before editing it with the set command:

$ sudo pibootctl save default
$ sudo pibootctl set camera.enabled=on gpu.mem=128
$ sudo pibootctl save cam

Note

Note that commands which modify the content of the boot partition (e.g. save and set) are executed with sudo as root privileges are typically required.

The configuration of pibootctl itself dictates where the stored configurations are placed on disk. By default this is under a “pibootctl” directory on the boot partition, but this can be changed in the pibootctl configuration. The application attempts to read its configuration from the following locations on startup:

  • /lib/pibootctl/pibootctl.conf
  • /etc/pibootctl.conf
  • $XDG_CONFIG_HOME/pibootctl.conf

The final location is only intended for developers working on pibootctl itself. The others should be used by packages providing pibootctl on your chosen OS.

Stored boot configurations are simply PKZIP files containing the files that make up the boot configuration (sometimes this is just the config.txt file, and sometimes other files may be included).

Note

In the event that your system is unable to boot (e.g. because of mis-configuration), you can restore a stored boot configuration simply by unzipping the stored configuration back into the root of the boot partition.

In other words, you can simply place your Pi’s SD card in a Windows or MAC OS X computer which should automatically mount the boot partition (which is the only partition that these OS’ will understand on the card), find the “pibootctl” folder and under there you should see all your stored configurations as .zip files. Unzip one of these into the folder above “pibootctl”, overwriting files as necessary and you have restored your boot configuration.

The diff command can be used to discover the differences between boot configurations:

$ pibootctl diff default
+------------------------+---------------+-------------+
| Name                   | <Current>     | default     |
|------------------------+---------------+-------------|
| boot.firmware.filename | 'start_x.elf' | 'start.elf' |
| boot.firmware.fixup    | 'fixup_x.dat' | 'fixup.dat' |
| camera.enabled         | on            | off         |
| gpu.mem                | 128 (Mb)      | 64 (Mb)     |
+------------------------+---------------+-------------+

Note

Some settings indirectly affect others. Even though we did not explicitly set boot.firmware.filename, setting camera.enabled affected its default value.

The help command can be used to display the help screen for each sub-command:

$ pibootctl help save
usage: pibootctl save [-h] [-f] name

Store the current boot configuration under a given name.

positional arguments:
  name         The name to save the current boot configuration under; can
               include any characters legal in a filename

optional arguments:
  -h, --help   show this help message and exit
  -f, --force  Overwrite an existing configuration, if one exists

Additionally, help will accept setting names to display information about the defaults and underlying commands each setting represents:

$ pibootctl help camera.enabled
      Name: camera.enabled
   Default: off
Command(s): start_x, start_debug, start_file, fixup_file

Enables loading the Pi camera module firmware. This implies that
start_x.elf (or start4x.elf) will be loaded as the GPU firmware rather than
the default start.elf (and the corresponding fixup file).

Note: with the camera firmware loaded, gpu.mem must be 64Mb or larger
(128Mb is recommended for most purposes; 256Mb may be required for complex
processing pipelines).

The list command can be used to display the content of the configuration store, and load to restore previously saved configurations:

$ pibootctl list
+---------+--------+---------------------+
| Name    | Active | Timestamp           |
|---------+--------+---------------------|
| cam     | x      | 2020-03-11 21:29:56 |
| default |        | 2020-03-11 21:29:13 |
+---------+--------+---------------------+
$ sudo pibootctl load default

diff

Synopsis
pibootctl diff [-h] [--json | --yaml | --shell] [left] right
Description

Display the settings that differ between two stored boot configurations, or between one stored boot configuration and the current configuration.

Options
left

The boot configuration to compare from, or the current configuration if omitted.

right

The boot configuration to compare against.

-h, --help

Show a brief help page for the command.

--json

Use JSON as the output format.

--yaml

Use YAML as the output format.

--shell

Use a tab-delimited output format suitable for the shell.

Usage

The diff command is used to display the differences between two boot configurations; either two stored configurations (if two names are supplied on the command line), or between the current boot configuration and a stored one (if one name is supplied on the command line):

$ sudo pibootctl save default
$ sudo pibootctl set video.hdmi0.group=1 video.hdmi0.mode=4
$ pibootctl diff default
+-------------------+----------------+--------------------+
| Name              | <Current>      | default            |
|-------------------+----------------+--------------------|
| video.hdmi0.group | 1 (CEA)        | 0 (auto from EDID) |
| video.hdmi0.mode  | 4 (720p @60Hz) | 0 (auto from EDID) |
+-------------------+----------------+--------------------+
$ sudo pibootctl save 720p
$ pibootctl diff default 720p
+-------------------+--------------------+----------------+
| Name              | default            | 720p           |
|-------------------+--------------------+----------------|
| video.hdmi0.group | 0 (auto from EDID) | 1 (CEA)        |
| video.hdmi0.mode  | 0 (auto from EDID) | 4 (720p @60Hz) |
+-------------------+--------------------+----------------+

For developers wishing to build on top of pibootctl, options are provided to produce the output in JSON (--json), YAML (--yaml), and shell-friendly (--shell):

$ pibootctl diff --json default 720p
{"video.hdmi0.mode": {"right": 4, "left": 0}, "video.hdmi0.group":
{"right": 1, "left": 0}}

get

Synopsis
pibootctl get [-h] [--json | --yaml | --shell] setting [setting ...]
Description

Query the status of one or more boot configuration settings. If a single setting is requested then just that value is output. If multiple values are requested then both setting names and values are output. This applies whether output is in the default, JSON, YAML, or shell-friendly styles.

Options
setting

The name(s) of the setting(s) to query; if a single setting is given its value alone is output, if multiple settings are queried the names and values of the settings are output.

-h, --help

Show a brief help page for the command.

--json

Use JSON as the output format.

--yaml

Use YAML as the output format.

--shell

Use a var=value output format suitable for the shell.

Usage

The get command is primarily of use to those wishing to build something on top of pibootctl; for end users wishing to query the current boot configuration the status command is of more use. When given a single setting to query the value of that setting is output on its own, in whatever output style is selected:

$ pibootctl get video.overscan.enabled
on
$ pibootctl get --json video.overscan.enabled
true

When given multiple settings, names and values of those settings are both output:

$ pibootctl get serial.enabled serial.baud serial.uart
+----------------+-------------------------+
| Name           | Value                   |
|----------------+-------------------------|
| serial.baud    | 115200                  |
| serial.enabled | on                      |
| serial.uart    | 0 (/dev/ttyAMA0; PL011) |
+----------------+-------------------------+
$ pibootctl get --json serial.enabled serial.baud serial.uart
{"serial.enabled": true, "serial.baud": 115200, "serial.uart": 0}

Note that wildcards are not permitted with this command, unlike with the status command.

help

Synopsis
pibootctl help [-h] [command | setting]
Description

With no arguments, displays the list of pibootctl commands. If a command name is given, displays the description and options for the named command. If a setting name is given, displays the description and default value for that setting.

Options
-h, --help

Show a brief help page for the command.

command

The name of the command to output help for. The full command name must be given; abbreviations are not accepted.

setting

The name of the setting to output help for.

If the setting is not recognized, and contains an underscore (‘_’) character, the utility will assume it is a config.txt configuration command and attempt to output help for the setting that corresponds to it. If multiple settings correspond, their names will be printed instead.

Usage

The help command is the default command, and thus will be invoked if pibootctl is called with no other arguments. However it can also be used to retrieve help for specific commands:

$ pibootctl help ls
usage: pibootctl list [-h] [--json | --yaml | --shell]

List all stored boot configurations.

optional arguments:
  -h, --help  show this help message and exit
  --json      Use JSON as the format
  --yaml      Use YAML as the format
  --shell     Use a var=value or tab-delimited format suitable for the
              shell

Alternatively, it can be used to describe settings:

$ pibootctl help boot.debug.enabled
      Name: boot.debug.enabled
   Default: off
Command(s): start_debug, start_file, fixup_file

Enables loading the debugging firmware. This implies that start_db.elf (or
start4db.elf) will be loaded as the GPU firmware rather than the default
start.elf (or start4.elf). Note that the debugging firmware incorporates
the camera firmware so this will implicitly switch camera.enabled on if it
is not already.

The debugging firmware performs considerably more logging than the default
firmware but at a performance cost, ergo it should only be used when
required.

Finally, if you are more familiar with the “classic” boot configuration commands, it can be used to discover which pibootctl settings correspond to those commands:

$ pibootctl help start_file
start_file is affected by the following settings:

camera.enabled
boot.debug.enabled
boot.firmware.filename

list

Synopsis
pibootctl list [-h] [--json | --yaml | --shell]
Description

List all stored boot configurations.

Options
-h, --help

Show a brief help page for the command.

--json

Use JSON as the output format.

--yaml

Use YAML as the output format.

--shell

Use a tab-delimited output format suitable for the shell.

Usage

The list command is used to display the content of the store of boot configurations:

$ pibootctl list
+---------+--------+---------------------+
| Name    | Active | Timestamp           |
|---------+--------+---------------------|
| 720p    | x      | 2020-03-10 11:33:24 |
| default |        | 2020-03-10 11:32:12 |
| dpi     |        | 2020-02-01 15:46:48 |
| gpi     |        | 2020-02-01 16:13:02 |
+---------+--------+---------------------+

If one (or more) of the stored configurations match the current boot configuration, this will be indicated in the “Active” column. Note that equivalence is based on a hash of all files in the configuration, not on the resulting settings. Hence a simple edit like, for example, reversing the order of two lines (which might not make any difference to the resulting settings) would be sufficient to mark the configuration as “different”.

The “timestamp” of a stored configuration is the last modification date of that configuration (calculated as the latest modification date of all files within the configuration).

For developers wishing to build on top of pibootctl, options are provided to produce the output in JSON (--json), YAML (--yaml), and shell-friendly (--shell). These combine with all aforementioned options as expected:

$ pibootctl list --json
[{"timestamp": "2020-02-01T15:46:48", "active": false, "name": "dpi"},
{"timestamp": "2020-03-10T11:32:12", "active": false, "name": "default"},
{"timestamp": "2020-02-01T16:13:02", "active": false, "name": "gpi"},
{"timestamp": "2020-03-10T11:33:24", "active": true, "name": "720p"}]

load

Synopsis
pibootctl load [-h] [--no-backup] name
Description

Overwrite the current boot configuration with a stored one.

Options
name

The name of the boot configuration to restore

-h, --help

Show a brief help page for the command.

--no-backup

Don’t take an automatic backup of the current boot configuration if one doesn’t exist

Usage

The load command is used to replace the current boot configuration with one previously stored. Effectively this simply unpacks the PKZIP of the stored boot configuration into the boot partition, overwriting existing files.

If the current boot configuration has not been stored (with the save command), an automatically named backup will be saved first:

$ sudo pibootctl save default
$ sudo pibootctl set video.hdmi0.group=1 video.hdmi0.mode=4
$ sudo pibootctl load default
Backed up current configuration in backup-20200310-095646

This can be avoided with the --no-backup option.

Warning

The command is written to guarantee that no files will ever be left half-written (files are unpacked to a temporary filename then atomically moved into their final location overwriting any existing file).

However, the utility cannot guarantee that in the event of an error, the configuration as a whole is not half-written (i.e. that one or more files failed to unpack). In other words, in the event of failure you cannot assume that the boot configuration is consistent.

remove

Synopsis
pibootctl remove [-h] [-f] name
Description

Remove a stored boot configuration.

Options
name

The name of the boot configuration to remove.

-h, --help

Show a brief help page for the command.

-f, --force

Ignore errors if the named configuration does not exist.

Usage

The remove command is used to delete a stored boot configuration:

$ pibootctl list
+---------+--------+---------------------+
| Name    | Active | Timestamp           |
|---------+--------+---------------------|
| 720p    | x      | 2020-03-10 11:33:24 |
| default |        | 2020-03-10 11:32:12 |
| dpi     |        | 2020-02-01 15:46:48 |
| gpi     |        | 2020-02-01 16:13:02 |
+---------+--------+---------------------+
$ sudo pibootctl remove gpi
$ pibootctl list
+---------+--------+---------------------+
| Name    | Active | Timestamp           |
|---------+--------+---------------------|
| 720p    | x      | 2020-03-10 11:33:24 |
| default |        | 2020-03-10 11:32:12 |
| dpi     |        | 2020-02-01 15:46:48 |
+---------+--------+---------------------+

If, for scripting purposes, you wish to ignore the error in the case the specified stored configuration does not exist, use the --force option:

$ pibootctl rm foo
unknown configuration foo
$ pibootctl rm -f foo

rename

Synopsis
pibootctl rename [-h] [-f] name to
Description

Rename a stored boot configuration.

Options
name

The name of the boot configuration to rename.

to

The new name of the boot configuration.

-h, --help

Show a brief help page for the command.

-f, --force

Overwrite the target configuration, if it exists.

Usage

The rename command can be used to change the name of a stored boot configuration:

$ pibootctl ls
+---------+--------+---------------------+
| Name    | Active | Timestamp           |
|---------+--------+---------------------|
| 720p    | x      | 2020-03-10 11:33:24 |
| default |        | 2020-03-10 11:32:12 |
| dpi     |        | 2020-02-01 15:46:48 |
+---------+--------+---------------------+
$ sudo pibootctl rename default foo
$ pibootctl ls
+------+--------+---------------------+
| Name | Active | Timestamp           |
|------+--------+---------------------|
| 720p | x      | 2020-03-10 11:33:24 |
| dpi  |        | 2020-02-01 15:46:48 |
| foo  |        | 2020-03-10 11:32:12 |
+------+--------+---------------------+

As with save, any characters permitted in a filename are permitted in the new destination name.

If you wish to rename a configuration such that it overwrites an existing configuration you will need to use the --force option:

$ sudo pibootctl load default
$ sudo pibootctl save foo
$ pibootctl ls
+---------+--------+---------------------+
| Name    | Active | Timestamp           |
|---------+--------+---------------------|
| 720p    |        | 2020-03-10 11:33:24 |
| default | x      | 2020-03-10 11:32:12 |
| dpi     |        | 2020-02-01 15:46:48 |
| foo     | x      | 2020-03-10 11:32:12 |
+---------+--------+---------------------+
$ sudo pibootctl mv foo default
[Errno 17] File exists: 'default.zip'
$ sudo pibootctl mv -f foo default

save

Synopsis
pibootctl save [-h] [-f] name
Description

Store the current boot configuration under a given name.

Options
name

The name to save the current boot configuration under; can include any characters legal in a filename

-h, --help

Show a brief help page for the command.

-f, --force

Overwrite an existing configuration, if one exists

Usage

The save command is used to take a backup of the current boot configuration. In practice this creates a PKZIP of the files that make up the boot configuration (config.txt et al.), and places it under the configured directory on the boot partition (usually pibootctl):

$ ls /boot/pibootctl
$ sudo pibootctl save foo
$ ls /boot/pibootctl
foo.zip

Note that by default, you cannot overwrite saved configurations, but this can be overridden with the --force option:

$ sudo pibootctl save foo
[Errno 17] File exists: 'foo.zip'
$ sudo pibootctl save -f foo

In the event that your system is rendered un-bootable, a boot configuration can be easily restored by extracting the PKZIP of a saved configuration into the boot partition (over-writing files as necessary). Alternatively you can use the load command (if the system can boot). The list command can be used to display all currently stored configurations.

set

Synopsis
pibootctl set [-h] [--no-backup] [--all | --this-model | --this-serial]
              [--json] [--yaml] [--shell]
              [name=[value] [name=[value] ...]]
Description

Change the value of one or more boot configuration settings. To reset the value of a setting to its default, simply omit the new value.

Options
name=[value]

Specify one or more settings to change on the command line; to reset a setting to its default omit the value.

-h, --help

Show a brief help page for the command.

--no-backup

Don’t take an automatic backup of the current boot configuration if one doesn’t exist.

--all

Set the specified settings on all Pis this SD card is used with. This is the default context.

--this-model

Set the specified settings for this model of Pi only.

--this-serial

Set the specified settings for this Pi’s serial number only.

--json

Use JSON as the input format.

--yaml

Use YAML as the input format.

--shell

Use a var=value input format suitable for the shell.

Usage

The set command can be used at the command line to update the boot configuration:

$ sudo pibootctl set video.overscan.enabled=off
Backed up current configuration in backup-20200309-230959

Note that, if no backup of the current boot configuration exists, a backup is automatically taken (unless --no-backup is specified). Multiple settings can be changed at once, and settings can be reset to their default value by omitting the new value after the “=” sign:

$ sudo pibootctl set --no-backup serial.enabled=on serial.uart=

By default, settings are written into an “[all]” section in config.txt meaning that they will apply everywhere the SD card is moved. However, you can opt to make settings specific to the current model of Pi, or even the current Pi’s serial number:

$ sudo pibootctl set --this-serial camera.enabled=on gpu.mem=128

In this case an appropriate section like “[0x123456789]” will be added and the settings written under there.

For those wishing to build an interface on top of pibootctl, JSON, YAML, and shell-friendly formats can also be used to feed new values to the set command:

$ cat << EOF | sudo pibootctl set --json --no-backup
{"serial.enabled": true, "serial.uart": null}
EOF

show

Synopsis
pibootctl show [-h] [-a] [--json | --yaml | --shell] name [pattern]
Description

Display the specified stored boot configuration, or the sub-set of its settings that match the specified pattern.

Options
name

The name of the boot configuration to display.

pattern

If specified, only displays settings with names that match the specified pattern which may include shell globbing characters (e.g. *, ?, and simple [classes])

-h, --help

Show a brief help page for the command.

-a, --all

Include all settings, regardless of modification, in the output; by default, only settings which have been modified are included.

--json

Use JSON as the output format.

--yaml

Use YAML as the output format.

--shell

Use a var=value output format suitable for the shell.

Usage

The show command is the equivalent of the status command for stored boot configurations. By default it displays only the settings in the specified configuration that have been modified from their default:

$ pibootctl show 720p
+------------------------+----------------+
| Name                   | Value          |
|------------------------+----------------|
| video.hdmi0.group      | 1 (CEA)        |
| video.hdmi0.mode       | 4 (720p @60Hz) |
+------------------------+----------------+

The full set of settings can be displayed (which is usually several pages long, and thus will implicitly invoke the system’s pager) can be displayed with the --all option:

$ pibootctl show 720p --all
+------------------------------+----------+--------------------------------+
| Name                         | Modified | Value                          |
|------------------------------+----------+--------------------------------|
...
| video.hdmi0.enabled          |          | auto                           |
| video.hdmi0.encoding         |          | 0 (auto; 1 for CEA, 2 for DMT) |
| video.hdmi0.flip             |          | 0 (none)                       |
| video.hdmi0.group            | x        | 1 (CEA)                        |
| video.hdmi0.mode             | x        | 4 (720p @60Hz)                 |
| video.hdmi0.mode.force       |          | off                            |
| video.hdmi0.rotate           |          | 0                              |
| video.hdmi0.timings          |          | []                             |
| video.hdmi1.audio            |          | auto                           |
| video.hdmi1.boost            |          | 5                              |
...

Note that when --all is specified, a “Modified” column is included in the output to indicate which settings are no longer default.

As with the status command, the list of settings can be further filtered by specified a pattern with the command. The pattern can include any of the common shell wildcard characters:

  • * for any number of any character
  • ? for any single character
  • [seq] for any character in seq
  • [!seq] for any character not in seq

For example:

$ pibootctl show --all 720p i2c.*
+-------------+----------+--------+
| Name        | Modified | Value  |
|-------------+----------+--------|
| i2c.baud    |          | 100000 |
| i2c.enabled |          | off    |
+-------------+----------+--------+

For developers wishing to build on top of pibootctl, options are provided to produce the output in JSON (--json), YAML (--yaml), and shell-friendly (--shell). These combine with all aforementioned options as expected:

$ pibootctl show --json --all 720p i2c.*
{"i2c.baud": 100000, "i2c.enabled": false}

status

Synopsis
pibootctl status [-h] [-a] [--json | --yaml | --shell] [pattern]
Description

Output the current value of modified boot time settings that match the specified pattern (or all if no pattern is provided). The --all option may be specified to output all boot settings regardless of modification state.

Options
pattern

If specified, only displays settings with names that match the specified pattern which may include shell globbing characters (e.g. *, ?, and simple [classes]).

-h, --help

Show a brief help page for the command.

-a, --all

Include all settings, regardless of modification, in the output. By default, only settings which have been modified are included.

--json

Use JSON as the output format.

--yaml

Use YAML as the output format.

--shell

Use a var=value format suitable for the shell.

Usage

By default, the status command only outputs boot time settings which have been modified:

$ pibootctl status
+-------------+-------+
| Name        | Value |
|-------------+-------|
| i2c.enabled | on    |
| spi.enabled | on    |
+-------------+-------+

The full set of settings (which is usually several pages long, and thus will implicitly invoke the system’s pager) can be displayed with the --all option:

$ pibootctl status --all
+------------------------------+----------+--------------------------+
| Name                         | Modified | Value                    |
|------------------------------+----------+--------------------------|
...
| i2c.baud                     |          | 100000                   |
| i2c.enabled                  | x        | on                       |
| i2s.enabled                  |          | off                      |
| serial.baud                  |          | 115200                   |
| serial.clock                 |          | 48000000                 |
| serial.enabled               |          | on                       |
| serial.uart                  |          | 0 (/dev/ttyAMA0; PL011)  |
| spi.enabled                  | x        | on                       |
| video.cec.enabled            |          | on                       |
...

Note that when --all is specified, a “Modified” column is included in the output to indicate which settings are no longer default.

The list of settings can be further filtered by specified a pattern with the command. The pattern can include any of the common shell wildcard characters:

  • * for any number of any character
  • ? for any single character
  • [seq] for any character in seq
  • [!seq] for any character not in seq

For example:

$ pibootctl status --all i2c.*
+-------------+----------+--------+
| Name        | Modified | Value  |
|-------------+----------+--------|
| i2c.baud    |          | 100000 |
| i2c.enabled | x        | on     |
+-------------+----------+--------+

For developers wishing to build on top of pibootctl, options are provided to produce the output in JSON (--json), YAML (--yaml), and shell-friendly (--shell). These combine with all aforementioned options as expected:

$ pibootctl status --json --all i2c.*
{"i2c.baud": 100000, "i2c.enabled": true}

Development

If you wish to install a copy of pibootctl for development purposes, clone the git repository and set up a configuration to use the cloned directory as the source of the boot configuration:

$ sudo apt install python3-dev git virtualenvwrapper
$ cd
$ git clone https://github.com/waveform80/pibootctl.git
$ mkvirtualenv -p /usr/bin/python3 pibootctl
$ cd pibootctl
$ workon pibootctl
(pibootctl) $ make develop
(pibootctl) $ cat > ~/.config/pibootctl.conf << EOF
[defaults]
boot_path=.
store_path=store
reboot_required=
reboot_required_pkgs=
EOF

At this point you should be able to call the pibootctl utility, and have it store the (empty) boot configuration as a PKZIP file under the working directory:

$ pibootctl save foo
$ pibootctl ls
+------+--------+---------------------+
| Name | Active | Timestamp           |
|------+--------+---------------------|
| foo  | x      | 2020-03-08 22:40:28 |
+------+--------+---------------------+

To work on the clone in future simply enter the directory and use the workon command:

$ cd ~/pibootctl
$ workon pibootctl

To pull the latest changes from git into your clone and update your installation:

$ cd ~/pibootctl
$ workon pibootctl
(pibootctl) $ git pull
(pibootctl) $ make develop

To remove your installation, destroy the sandbox and the clone:

(pibootctl) $ cd
(pibootctl) $ deactivate
$ rmvirtualenv pibootctl
$ rm -fr ~/pibootctl

Building the docs

If you wish to build the docs, you’ll need a few more dependencies. Inkscape is used for conversion of SVGs to other formats, Graphviz and Gnuplot are used for rendering certain charts, and TeX Live is required for building PDF output. The following command should install all required dependencies:

$ sudo apt install texlive-xetex fonts-freefont-otf graphviz gnuplot inkscape

Once these are installed, you can use the “doc” target to build the documentation:

$ cd ~/pibootctl
$ workon pibootctl
(pibootctl) $ make doc

The HTML output is written to build/html while the PDF output goes to build/latex.

Test suite

If you wish to run the test suite, follow the instructions in Development above and then make the “test” target within the sandbox:

$ cd ~/pibootctl
$ workon pibootctl
(pibootctl) $ make test

A tox configuration is also provided that will test the utility against all supported Python versions:

$ cd ~/pibootctl
$ workon pibootctl
(pibootctl) $ pip install tox
...
(pibootctl) $ tox -p auto

Note

If developing under Ubuntu, the Dead Snakes PPA is particularly useful for obtaining additional Python installations for testing.

API

pibootctl can be used both as a standalone application, and as an API within Python. The primary class of interest when using pibootctl as an API is Store in the pibootctl.store module, but pibootctl.main is useful for providing an instance of this, constructed from the pibootctl configuration.

The API is split into several modules, documented in the following sections:

pibootctl.exc

The pibootctl.exc module defines the various exceptions used in the application:

exception pibootctl.exc.InvalidConfiguration(errors)[source]

Error raised when an updated configuration fails to validate. All ValueError exceptions raised during validation are available from the errors attribute which maps setting names to the ValueError raised.

exception pibootctl.exc.IneffectiveConfiguration(diff)[source]

Error raised when an updated configuration has been overridden by something in a file we’re not allowed to edit. All settings which have been overridden are available from the diff attribute.

pibootctl.files

The pibootctl.files module contains the AtomicReplaceFile context manager, used to “safely” replace files by writing to a temporary file in the same directory, then moving the result over the target if no exception occurs within the block. The result is that external processes either see the “old” state of the file, or the “new” state, but nothing in between:

>>> from pathlib import Path
>>> from pibootctl.files import AtomicReplaceFile
>>> foo = Path('foo.txt')
>>> foo.write_text('foo')
>>> foo.read_text()
'foo'
>>> with AtomicReplaceFile(foo, encoding='ascii') as f:
...     f.write('bar')
...     raise Exception('something went wrong!')
...
3
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
Exception: something went wrong!
>>> foo.read_text()
'foo'
class pibootctl.files.AtomicReplaceFile(path, encoding=None)[source]

A context manager for atomically replacing a target file.

Uses tempfile.NamedTemporaryFile() to construct a temporary file in the same directory as the target file. The associated file-like object is returned as the context manager’s variable; you should write the content you wish to this object.

When the context manager exits, if no exception has occurred, the temporary file will be renamed over the target file atomically (and sensible permissions will be set, i.e. 0666 & umask). If an exception occurs during the context manager’s block, the temporary file will be deleted leaving the original target file unaffected and the exception will be re-raised.

Parameters:
  • path (str or pathlib.Path) – The full path and filename of the target file. This is expected to be an absolute path.
  • encoding (str) – If None (the default), the temporary file will be opened in binary mode. Otherwise, this specifies the encoding to use with text mode.

pibootctl.formatter

The pibootctl.formatter module contains some generic text formatting routines, including the TableWrapper class (akin to TextWrapper but specific to table output), TransMap for partially formatting templates, and the render() function: a crude markup renderer.

class pibootctl.formatter.TableWrapper(width=70, header_rows=1, footer_rows=0, cell_separator=' ', internal_line='-', internal_separator=' ', borders=('', '', '', ''), corners=('', '', '', ''), internal_borders=('', '', '', ''), align=None, format=None)[source]

Similar to TextWrapper, this class provides facilities for wrapping text to a particular width, but with a focus on table-based output.

The constructor takes numerous arguments, but typically you don’t need to specify them all (or at all). A series of dictionaries are provided with “common” configurations: pretty_table, curvy_table, unicode_table, and curvy_unicode_table. For example:

>>> from pibootctl.formatter import *
>>> wrapper = TableWrapper(width=80, **curvy_table)
>>> data = [
... ('Name', 'Length', 'Position'),
... ('foo', 3, 1),
... ('bar', 3, 2),
... ('baz', 3, 3),
... ('quux', 4, 4)]
>>> print(wrapper.fill(data))
,------+--------+----------.
| Name | Length | Position |
|------+--------+----------|
| foo  | 3      | 1        |
| bar  | 3      | 2        |
| baz  | 3      | 3        |
| quux | 4      | 4        |
`------+--------+----------'

The TableWrapper instance attributes (and keyword arguments to the constructor) are as follows:

width

(default 70) The maximum number of characters that the table can take up horizontally. TableWrapper guarantees that no output line will be longer than width characters.

header_rows

(default 1) The number of rows at the top of the table that will be separated from the following rows by a horizontal border (internal_line).

footer_rows

(default 0) The number of rows at the bottom of the table that will be separated from the preceding rows by a horizontal border (internal_line).

cell_separator

(default ' ') The string used to separate columns of cells.

internal_line

(default '-') The string used to draw horizontal lines inside the table for header_rows and footer_rows.

internal_separator

(default ' ') The string used within runs of internal_line to separate columns.

borders

(default ('', '', '', '')) A 4-tuple of strings which specify the characters used to create the left, top, right, and bottom borders of the table respectively.

corners

(default ('', '', '', '')) A 4-tuple of strings which specify the characters used for the top-left, top-right, bottom-right, and bottom-left corners of the table respectively.

internal_borders

(default ('', '', '', '')) A 4-tuple of strings which specify the characters used to interrupt runs of the borders characters to draw row and column separators. Like borders these are the left, top, right, and bottom characters respectively.

align

A callable accepting three parameters: 0-based row index, 0-based column index, and the cell data. The callable must return a character indicating the intended alignment of data within the cell. “<” for left justification, “^” for centered alignment, and “>” for right justification (as in str.format()). The default is to left align everything.

format

A callable accepting three parameters: 0-based row index, 0-based column index, and the cell data. The callable must return the desired string representation of the cell data. The default simply calls str on everything.

TableWrapper also provides similar public methods to TextWrapper:

wrap(data)[source]

Wraps the table data returning a list of output lines without final newlines. data must be a sequence of row tuples, each of which is assumed to be the same length.

If the current width does not permit at least a single character per column (after taking account of the width of borders, internal separators, etc.) then ValueError will be raised.

fill(data)[source]

Wraps the table data returning a string containing the wrapped output.

pibootctl.formatter.pretty_table

Uses simple ASCII characters to produce a typical “box-like” table appearance:

>>> from pibootctl.formatter import *
>>> wrapper = TableWrapper(width=80, **pretty_table)
>>> data = [
... ('Name', 'Length', 'Position'),
... ('foo', 3, 1),
... ('bar', 3, 2),
... ('baz', 3, 3),
... ('quux', 4, 4)]
>>> print(wrapper.fill(data))
+------+--------+----------+
| Name | Length | Position |
|------+--------+----------|
| foo  | 3      | 1        |
| bar  | 3      | 2        |
| baz  | 3      | 3        |
| quux | 4      | 4        |
+------+--------+----------+
pibootctl.formatter.curvy_table

Uses simple ASCII characters to produce a “round-edged” table appearance:

>>> from pibootctl.formatter import *
>>> wrapper = TableWrapper(width=80, **curvy_table)
>>> data = [
... ('Name', 'Length', 'Position'),
... ('foo', 3, 1),
... ('bar', 3, 2),
... ('baz', 3, 3),
... ('quux', 4, 4)]
>>> print(wrapper.fill(data))
,------+--------+----------.
| Name | Length | Position |
|------+--------+----------|
| foo  | 3      | 1        |
| bar  | 3      | 2        |
| baz  | 3      | 3        |
| quux | 4      | 4        |
`------+--------+----------'
pibootctl.formatter.unicode_table

Uses unicode box-drawing characters to produce a typical “box-like” table appearance:

>>> from pibootctl.formatter import *
>>> wrapper = TableWrapper(width=80, **unicode_table)
>>> data = [
... ('Name', 'Length', 'Position'),
... ('foo', 3, 1),
... ('bar', 3, 2),
... ('baz', 3, 3),
... ('quux', 4, 4)]
>>> print(wrapper.fill(data))
┌──────┬────────┬──────────┐
│ Name │ Length │ Position │
├──────┼────────┼──────────┤
│ foo  │ 3      │ 1        │
│ bar  │ 3      │ 2        │
│ baz  │ 3      │ 3        │
│ quux │ 4      │ 4        │
└──────┴────────┴──────────┘
pibootctl.formatter.curvy_unicode_table

Uses unicode box-drawing characters to produce a “round-edged” table appearance:

>>> from pibootctl.formatter import *
>>> wrapper = TableWrapper(width=80, **curvy_unicode_table)
>>> data = [
... ('Name', 'Length', 'Position'),
... ('foo', 3, 1),
... ('bar', 3, 2),
... ('baz', 3, 3),
... ('quux', 4, 4)]
>>> print(wrapper.fill(data))
╭──────┬────────┬──────────╮
│ Name │ Length │ Position │
├──────┼────────┼──────────┤
│ foo  │ 3      │ 1        │
│ bar  │ 3      │ 2        │
│ baz  │ 3      │ 3        │
│ quux │ 4      │ 4        │
╰──────┴────────┴──────────╯
class pibootctl.formatter.TransMap(**kw)[source]

Used with str.format_map() to substitute only a subset of values in a given template, passing the rest through for later processing. For example:

>>> '{foo}{bar}'.format_map(TransMap(foo=1))
'1{bar}'
>>> '{foo:02d}{bar:02d}{baz:02d}'.format_map(TransMap(foo=1, baz=3))
'01{bar:02d}03'

Note

One exception is that the !a conversion is not handled correctly. This is erroneously converted to !r. Unfortunately there’s no solution to this; it’s a side-effect of the means by which the !a conversion is performed.

class pibootctl.formatter.FormatDict(data, key_title='Key', value_title='Value', sort_key=None)[source]

Used to format data, a dict, in a format acceptable as input to the render() function. The key_title and value_title strings provide the cells for the single header row.

This class is intended to be used within a string for str.format(). For example:

>>> from pibootctl.formatter import FormatDict
>>> d = {'foo': 100, 'bar': 200}
>>> print('An example table:\n\n{s}'.format(s=FormatDict(d)))
An example table:

| Key | Value |
| foo | 100 |
| bar | 200 |

The format specification in the format string can be used to request different kinds of output, for instance:

>>> f = FormatDict({'foo': 100, 'bar': 200})
>>> print('An example list:\n\n{f:list}'.format(f=f))
An example list:

* foo = 100
* bar = 200
>>> print('An example reference list:\n\n{f:refs}'.format(f=f))
An example reference list:

[foo]: 100
[bar]: 200

The default format specification is “table”, naturally.

If the values are tuples that should be expanded into multiple columns, set value_title to a tuple with the corresponding column titles:

>>> from pibootctl.formatter import FormatDict
>>> d = {'foo': (1, 100), 'bar': (2, 200)}
>>> print('An example table:\n\n{s}'.format(s=FormatDict(d,
... value_title=('col1', 'col2'))))
An example table:

| Key | col1 | col2 |
| foo | 1 | 100 |
| bar | 2 | 200 |

Tuple values are only supported for table output.

Note

In Python versions before 3.7, you may need to use collections.OrderedDict to ensure output of the elements of data in a particular order. Alternatively, you may specify a sort_key value which will be applied to the key values of the dict to sort them prior to output.

pibootctl.formatter.int_ranges(values, range_sep='-', list_sep=', ')[source]

Given a set of integer values, returns a compressed string representation of all values in the set. For example:

>>> int_ranges({1, 2})
'1, 2'
>>> int_ranges({1, 2, 3})
'1-3'
>>> int_ranges({1, 2, 3, 4, 8})
'1-4, 8'
>>> int_ranges({1, 2, 3, 4, 8, 9})
'1-4, 8-9'

range_sep and list_sep can be optionally specified to customize the strings used to separate ranges and lists of ranges respectively.

pibootctl.formatter.render(text, width=70, list_space=False, table_style=None)[source]

A crude renderer for a crude markup language intended for formatting documentation for the console.

The markup recognized by this routine is as follows:

* Paragraphs must be separated by at least one blank line. They will be
  wrapped to *width*.

* Items in bulleted lists must start with an asterisk. No list nesting
  is permitted, but items may span several lines (without blank lines
  between them). Items will be wrapped to *width* and indented
  appropriately.

* Lines beginning and ending with a pipe character are assumed to be
  table rows. Pipe characters also delimit columns within the row. The
  first row is assumed to be a header row and will be separated from
  the rest.

An example table is shown below:

| Command | Description |
| cd | changes the current directory |
| ls | lists the content of a directory |
| cp | copies files |
| mv | renames files |
| rm | removes files |

pibootctl.info

The pibootctl.info module contains some simple routines for determining information about the Pi that the application is running on.

pibootctl.info.get_board_revision()[source]

Return the Pi’s board revision as an unsigned 32-bit integer number. This is the same number as reported under “Revision” in /proc/cpuinfo.

pibootctl.info.get_board_serial()[source]

Return the Pi’s serial number as an unsigned 64-bit integer number. This can also be queried as “Serial” under /proc/cpuinfo.

pibootctl.info.get_board_type()[source]

Return a string indicating the overall model of the Pi, e.g. “pi0w”, “pi2”, or “pi3+”. This is derived from the result of get_board_revision() according to the Pi’s revision codes table.

pibootctl.info.get_board_types()[source]

Return a set of strings used for matching the model of Pi against configuration sections according to the conditional filters table.

pibootctl.info.get_board_mem()[source]

Return the amount of memory (in megabytes) present on the Pi, according to the model returned by get_board_revision().

pibootctl.main

The pibootctl.main module defines the Application class, and an instance of this called main. Instances of Application are callable and thus main is the entry-point for the pibootctl script.

From an API perspective, this module is primarily useful for providing an instance of the Store class:

from pibootctl.main import main
from pibootctl.store import Store, Current, Default

store = main.store
store[Current] = store['foo']
pibootctl.main.main

The instance of Application which is the entry-point for the pibootctl script.

class pibootctl.main.Application[source]

An instance of this class (main) is the entry point for the application. The instance is callable, accepting the command line arguments as its single (optional) argument. The arguments will be derived from sys.argv if not provided:

>>> from pibootctl.main import main
>>> try:
...     main(['-h'])
... except SystemExit:
...     pass
usage:  [-h] [--version]
{help,?,status,dump,get,set,save,load,diff,show,cat,list,ls,remove,rm,rename,mv}
...

Warning

Calling main will raise SystemExit in several cases (usually when requesting help output). It will also replace the system exception hook (sys.excepthook()).

This is intended and by design. If you wish to use pibootctl as an API, you are better off investigating the Store class, or treating pibootctl as a self-contained script and calling it with subprocess.

backup_if_needed()[source]

Tests whether the active boot configuration is also present in the store (by checking for the calculated hash). If it isn’t, constructs a unique filename (backup-<timestamp>) and saves a copy of the active boot configuration under it.

do_diff()[source]

Implementation of the diff command.

do_get()[source]

Implementation of the get command.

do_help()[source]

Implementation of the help command.

do_list()[source]

Implementation of the list command.

do_load()[source]

Implementation of the load command.

do_remove()[source]

Implementation of the remove command.

do_rename()[source]

Implementation of the rename command.

do_save()[source]

Implementation of the save command.

do_set()[source]

Implementation of the set command.

do_show()[source]

Implementation of the show command.

do_status()[source]

Implementation of the status command.

static invalid_config(*exc)[source]

Generates the error message for unhandled InvalidConfiguration exceptions. These are caused when a configuration fails to validate, and have an errors attribute listing all the exceptions that occurred during validation.

mark_reboot_required()[source]

Writes the necessary files to indicate that the system requires a reboot.

static overridden_config(*exc)[source]

Generates the error message for unhandled IneffectiveConfiguration exceptions. These are caused when a boot configuration is split across multiple files; the application is permitted to modify a file before the final one, but a later file overrides a value the application has tried to set in the file it is permitted to modify.

static permission_error(*exc)[source]

Generates the error message for unhandled PermissionError exceptions. As these are very likely to be caused by non-root execution, this is customzied to warn about this in the event that the effective UID is not 0.

commands

A dictionary mapping command names to their sub-parser.

config

Returns the script’s configuration as derived from the files in the three pre-defined locations (see pibootctl for more information). Returns a Namespace containing the parsed configuration.

parser

The parser for all the sub-commands that the script accepts. The parser’s defaults are derived from the configuration obtained from config. Returns the newly constructed argument parser.

store

The Store containing the current and stored boot configurations.

pibootctl.parser

The pibootctl.parser module provides the BootParser class for parsing the boot configuration of the Raspberry Pi.

The output of this class consists of derivatives of BootLine (BootSection, BootCommand, etc.) and BootFile instances, which in turn reference BootConditions instances to indicate the context in which they were found.

class pibootctl.parser.BootParser(path)[source]

Parser for the files used to configure the Raspberry Pi’s bootloader.

The path specifies the container of all files that make up the configuration. It be one of:

  • a str or a Path in which case the path specified must be a directory
  • a ZipFile
  • a dict mapping filenames to BootFile instances; effectively the output of files after parsing
add(filename, encoding=None, errors=None)[source]

Adds the auxilliary filename under path to the configuration. This is used to update the hash and files of the parsed configuration to include files which are referenced by the boot configuration but aren’t themselves configuration files (e.g. EDID data, and the kernel cmdline.txt).

If specified, encoding and errors are as for open(). If encoding is None, the data is assumed to be binary and the method will return the content of the file as a bytes string. Otherwise, the content of the file is assumed to be text and will be returned as a list of str.

parse(filename='config.txt')[source]

Parse the boot configuration on path. The optional filename specifies the “root” of the configuration, and defaults to config.txt.

If parsing is successful, this will update the files, hash, timestamp, and config attributes.

config

The parsed configuration; a sequence of BootLine instances (or derivatives of BootLine), after parse() has been successfully called.

files

The content of all parsed files; a mapping of filename to BootFile objects.

hash

After parse() is successfully called, this is the SHA1 hash of the complete configuration in parsed order (i.e. starting at “config.txt” and proceeding through all included files).

path

The path under which all configuration files can be found. This may be a Path instance, or a ZipFile, or a dict.

timestamp

The latest modified timestamp on all files that were read as a result of calling parse().

class pibootctl.parser.BootLine(filename, linenum, conditions, comment=None)[source]

Represents a line in a boot configuration. This is effectively an abstract base class and should never appear in output itself. Provides four attributes:

filename

A str indicating the path (relative to the configuration’s root) of the file containing the line.

linenum

The 1-based line number of the line.

conditions

A BootConditions specifying the filters in effect for this configuration line.

comment

Any comment that appears after other content on the line, or None if no comment was present

class pibootctl.parser.BootSection(filename, linenum, conditions, section, comment=None)[source]

A derivative of BootLine for [conditional sections] in a boot configuration. Adds a single attribute:

section

The criteria of the section (everything between the square brackets).

Note

The conditions for a BootSection instance includes the filters defined by that section.

class pibootctl.parser.BootCommand(filename, linenum, conditions, command, params, hdmi=None, comment=None)[source]

A derivative of BootLine which represents a command in a boot configuration, e.g. “disable_overscan=1”. Adds several attributes:

command

The title of the command; characters before the first “=” in the line.

params

The value of the command; characters after the first “=” in the line. As a special case, the “initramfs” command has two values and thus if command is “initramfs” then this attribute will be a 2-tuple.

hdmi

The HDMI display that the command applies to. This is usually None unless the command has an explicit hdmi suffix (“:” separated after the command title but before the “=”), or the command appears in an [HDMI:1] section.

class pibootctl.parser.BootInclude(filename, linenum, conditions, include, comment=None)[source]

A derivative of BootLine representing an “include” command in a boot configuration. Adds a single attribute:

include

The name of the file to be included.

class pibootctl.parser.BootFile[source]

Represents a file in a boot configuration.

filename

A str representing the file’s path relative to the boot configuration’s container (whatever that may be: a path, a zip archive, etc.)

timestamp

A datetime containing the last modification timestamp of the file.

Note

This is rounded down to a 2-second precision as that is all that PKZIP archives support.

content

A bytes string containing the complete content of the file.

encoding

None if the file is a binary file. Otherwise, specifies the name of the character encoding to be used when reading the file.

errors

None if the file is a binary file. Otherwise, specifies the character replacement strategy to be used with erroneous characters encountered when reading the file.

class pibootctl.parser.BootConditions[source]

Represents the set of conditional filters that apply to a given BootLine. The class implements methods necessary to compare instances as if they were sets.

For example:

>>> cond_all = BootConditions()
>>> cond_pi3 = BootConditions(pi='pi3')
>>> cond_pi3p = BootConditions(pi='pi3p')
>>> cond_serial = BootConditions(pi='pi3', serial=0x12345678)
>>> cond_all == cond_pi3
False
>>> cond_all >= cond_pi3
True
>>> cond_pi3 > cond_pi3p
True
>>> cond_serial < cond_pi3
True
>>> cond_serial < cond_pi3p
False
pi

The model of pi that the section applies to. See conditional filters for details of valid values. This represents sections like [pi3].

hdmi

The index of the HDMI port (0 or 1) that settings within this section will apply to, if no index-suffix is provided by the setting itself. This represents sections like [HDMI:0].

edid

The EDID of the display that the section applies to. This represents sections like [EDID=VSC-TD2220].

serial

The serial number of the Pi that settings within this section will apply to, stored as an int. This represents sections like [0x12345678].

gpio

The GPIO number and state that must be matched for settings in this section to apply, stored as a (gpio, state) tuple. This represents sections like [gpio2=0].

none

If this is True then a [none] section has been encountered and no settings apply.

suppress_count

This is a “suppression count” used to track sections within included files that are currently disabled (because the include occurred within a section that itself is disabled).

evaluate(section)[source]

Calculates a new conditional state (based upon the current conditional state) from the specified section criteria. Returns a new BootConditions instance.

generate(context=None)[source]

Given context, a BootConditions instance representing the currently active conditional sections, this method yields the conditional secitons required to set the conditions to this instance. If context is not specified, it defaults to conditions equivalent to [any], which is the default in the Pi bootloader.

For example:

>>> current = BootConditions(pi='pi2', gpio=(4, True))
>>> wanted = BootConditions()
>>> print('\n'.join(wanted.generate(current)))
[all]
>>> wanted = BootConditions(pi='pi4')
>>> print('\n'.join(wanted.generate(current)))
[all]
[pi4]
>>> current = BootConditions(pi='pi2')
>>> print('\n'.join(wanted.generate(current)))
[pi4]
>>> current = BootConditions(none=True)
>>> print('\n'.join(wanted.generate(current)))
[all]
[pi3]

Note

The yielded strings do not end with a line terminator.

suppress()[source]

If the current boot conditions are not enabled, returns a new BootConditions instance with the suppression count incremented by one. This is used during parsing to disable all conditionals in suppressed includes.

enabled

Returns True if parsed items are currently effective. If this is False, parsed items are ignored.

pibootctl.setting

The pibootctl.setting module defines all the classes used to represent boot configuration settings:

_images/setting_hierarchy.svg

The base of the hierarchy is Setting but this is effectively an abstract class and it is rare that anyone will need to use it directly. Rather you should derive from one of the concrete implementations below it like OverlayParam, Command, or one of the type-specializations like CommandBool, CommandInt, etc.

Note

For the sake of brevity, only the generic classes defined in pibootctl.setting are documented here. There are also specialization classes specific to individual settings defined (for cases of complex inter-dependencies, e.g. how the Bluetooth enabled status affects the default serial UART).

Developers are advised to familiarize themselves with the full range of classes in this module before defining additional ones.

class pibootctl.setting.Setting(name, *, default=None, doc='')[source]

Represents a configuration setting.

Each setting has a name which uniquely identifies the setting, a default value, and an optional doc string. The life-cycle of a typical setting in the scenario where the active boot configuration is being changed is:

  • extract() the value of a setting from parsed configuration lines
  • update() the value of a setting from user-provided values
  • validate() a setting in the wider context of a configuration
  • generate output() to represent the setting in a new config.txt

Optionally:

  • hint may be queried to describe a value in human-readable terms
extract(config)[source]

Given a config which must be a sequence of BootLine items, yields each line that potentially affects the setting’s value (including those currently disabled by conditionals), and the new value that the line produces (or None indicating that the value is now, or is still, the default state).

Note

This method is not influenced by conditionals that disable a line. In this case the method must still yield the line and the value it would produce (were it enabled). The caller will deal with the fact the line is currently disabled (but needs to be aware of such lines for the configuration mutator).

For this reason (and others) this method must not affect value directly; the caller will handle mutating the value when required.

output()[source]

Yields lines of configuration to represent the current state of the setting (taking in account the context of other Settings).

update(value)[source]

Given a value, returns it transformed to the setting’s native type (typically an int or bool but can be whatever type is appropriate).

The value may be a regular type (str, int, None, etc.) as deserialized from one of the input formats (JSON or YAML). Alternatively, it may be a UserStr, indicating that the value is a string given by the user on the command line and should be interpreted by the setting accordingly.

Note

Note to implementers: the method must not affect value directly; the caller will handle this.

validate()[source]

Validates the setting within the context of the other Settings. Raises ValueError in the event that the current value is invalid. May optionally use ValueWarning to warn about dangerous or inappropriate configurations.

default

The default value of this setting. The default may be sensitive to the wider context of Settings (i.e. the default of one setting can change depending on the current value of other settings).

doc

A description of the setting, used as help-text on the command line.

hint

Provides a human-readable interpretation of the state of the setting. Used by the “dump” and “show” commands to provide translations of default and current values.

Must return None if no explanation is available or necessary. Otherwise, must return a str.

key

Returns a tuple of strings which will be used to order the output of output() in the generated configuration.

Note

The output of this property must be unique for each setting, unless a setting delegates all its output to another setting.

lines

Returns the BootLine items which (if enabled by conditionals) affected the value of the setting, in the reverse order they were encountered while parsing (so the first enabled item holds the current value).

modified

Returns True when the setting has been modified. Note that it is not sufficient to simply compare value to default as some defaults are context- or platform-specific.

name

The name of the setting. This is a dot-delimited list of strings; note that the individual components do not have to be valid identifiers. For example, “boot.kernel.64bit”.

value

Returns the current value of the setting (or the default if the setting has not been modified).

class pibootctl.setting.Overlay(name, *, overlay, default=False, doc='')[source]

Represents a boolean setting that is “on” if the represented overlay is present, and “off” otherwise.

overlay

The name of the overlay this parameter affects.

class pibootctl.setting.OverlayParam(name, *, overlay='base', param, default=None, doc='')[source]

Represents a param to a device-tree overlay. Like Setting, this is effectively an abstract base class to be derived from.

param

The name of the parameter within the base overlay that this setting represents.

class pibootctl.setting.OverlayParamInt(name, *, overlay='base', param, default=0, doc='', valid=None)[source]

Represents an integer parameter to a device-tree overlay.

The valid parameter may optionally provide a dictionary mapping valid integer values for the command to string explanations, to be provided by the basic hint implementation.

class pibootctl.setting.OverlayParamBool(name, *, overlay='base', param, default=False, doc='')[source]

Represents a boolean parameter to the base device-tree overlay.

class pibootctl.setting.Command(name, *, command=None, commands=None, default=None, doc='', index=None)[source]

Represents a string-valued configuration command or commmands (one of these must be specified, but not both). If multiple commands are represented, only the first will be generated by output() in this base class.

This is also the base class for most simple-valued configuration commands (integer, boolean, etc).

commands

The configuration commands that this setting represents.

index

The index of this setting for multi-valued settings (e.g. settings which apply to HDMI outputs).

class pibootctl.setting.CommandInt(name, *, command=None, commands=None, default=0, doc='', index=0, valid=None)[source]

Represents an integer-valued configuration command or commands.

The valid parameter may optionally provide a dictionary mapping valid integer values for the command to string explanations, to be provided by the basic hint implementation.

class pibootctl.setting.CommandIntHex(name, *, command=None, commands=None, default=0, doc='', index=0, valid=None)[source]

An integer-valued configuration command or commands that are typically represented in hexi-decimal (like memory addresses).

class pibootctl.setting.CommandBool(name, *, command=None, commands=None, default=False, doc='', index=0)[source]

Represents a boolean-valued configuration command or commands.

class pibootctl.setting.CommandBoolInv(name, *, command=None, commands=None, default=False, doc='', index=0)[source]

Represents a boolean-valued configuration command or commands with inverted logic, e.g. video.overscan.enabled represents the disable_overscan setting and therefore its value is always the opposite of the actual written value.

class pibootctl.setting.CommandForceIgnore(name, *, force, ignore, doc='', index=0)[source]

Represents the tri-valued configuration values with force and ignore commands, e.g. hdmi_force_hotplug and hdmi_ignore_hotplug.

For these cases, when both commands are “0” the setting is considered to have the value None (which in most cases means “determine automatically”). When the force command is “1”, the setting is True and thus when the ignore command is “1”, the setting is False. When both are “1” (a contradictory setting) the final setting encountered takes precedence.

force

The boolean command that forces this setting on.

ignore

The boolean command that forces this setting off.

class pibootctl.setting.CommandMaskMaster(name, *, mask, command=None, commands=None, default=0, doc='', index=0, valid=None, dummies=())[source]

Represents an integer bit-mask setting as several settings. The “master” setting is the only one that produces any output. It defines the suffixes of its dummies (instances of CommandMaskDummy which parse the same setting but produce no output of their own).

The mask specifies the integer bit-mask to be applied to the underlying value for this setting. The right-shift will be calculated from this. Single-bit masks will be represented as boolean values rather than integers.

class pibootctl.setting.CommandMaskDummy(name, *, mask, command=None, commands=None, default=0, doc='', index=0, valid=None, dummies=())[source]

Represents portions of integer bit-masks which are subordinate to a CommandMaskMaster setting.

class pibootctl.setting.CommandFilename(name, *, command=None, commands=None, default=None, doc='', index=None)[source]

Represents settings that contain a filename affected by the os_prefix command. The filename returns the full filename incorporating the value of “boot.prefix” (if set), and hint outputs a suitable message including the full path.

filename

The full filename represented by the value, after concatenating it with the value of “boot.prefix”.

class pibootctl.setting.CommandIncludedFile(name, *, command=None, commands=None, default=None, doc='', index=None)[source]

Represents settings that reference a file which should be included in any stored boot configuration.

exception pibootctl.setting.ParseWarning[source]

Warning class used by Setting.extract() to warn about invalid values while parsing.

exception pibootctl.setting.ValueWarning[source]

Warning class used by Setting.validate() to warn about dangerous or inappropriate configurations.

pibootctl.settings

The pibootctl.settings module defines the template of all settings stored by the pibootctl.store.Settings class. Users of the API never have any need for this module, but developers wishing to extend the set of settings will need to modify the SETTINGS set.

pibootctl.settings.SETTINGS

A dict mapping setting names to pibootctl.setting.Setting instances which represents the complete set of settings that the application handles.

pibootctl.store

The pibootctl.store module defines classes which control a store of Raspberry Pi boot configurations, or the active boot configuration.

The main class of interest is Store. From an instance of this, one can access derivatives of BootConfiguration for the purposes of manipulating the store of configurations, or the active boot configuration itself. Each BootConfiguration contains an instance of Settings which maps setting names to Setting instances.

See pibootctl.main for information on obtaining an instance of Store.

pibootctl.store.Current[source]

The key of the active boot configuration in instances of Store.

pibootctl.store.Default[source]

The key of the default (empty) boot configuration in instances of Store.

class pibootctl.store.Store(boot_path, store_path, config_root='config.txt', mutable_files=frozenset({'config.txt'}), comment_lines=False)[source]

A mapping representing all boot configurations (current, default, and stored).

Acts as a mapping keyed by the name of the stored configuration, or the special values Current for the current boot configuration, or Default for the default (empty) configuration. The values of the mapping are derivatives of BootConfiguration which provide the parsed Settings, along with some other attributes.

The mapping is mutable and this can be used to manipulate stored boot configurations. For instance, to store the current boot configuration under the name “foo”:

>>> store = Store('/boot', 'pibootctl')
>>> store["foo"] = store[Current]

Setting the item with the key Current overwrites the current boot configuration:

>>> store[Current] = store["serial"]

Note that items retrieved from the store are effectively immutable; modifying them (even internally) does not modify the content of the store. To modify the content of the store, you must request a mutable() copy of a configuration, modify it, and assign it back:

>>> foo = store["foo"].mutable()
>>> foo.update({"serial.enabled": True})
>>> store["serial"] = foo

The same applies to the current boot configuration item:

>>> current = store[Current].mutable()
>>> current.update({"camera.enabled": True, "gpu.mem": 128})
>>> store[Current] = current

Items can be deleted to remove them from the store, with the obvious exception of the items with the keys Current and Default which cannot be removed (attempting to do so will raise a KeyError). Furthermore, the item with the key Default cannot be modified either.

Parameters:
  • boot_path (str) – The path on which the boot partition is mounted.
  • store_path (str) – The path (relative to boot_path) under which stored configurations will be saved.
  • config_root (str) – The filename of the “root” of the configuration, i.e. the first file read by the parser, and the file in which certain commands (e.g. start_x) must be placed. Currently, this should always be “config.txt”, the default.
  • mutable_files (set) – The set of filenames which MutableConfiguration instances are permitted to change. By default this is just “config.txt”.
  • comment_lines (bool) – If True, then MutableConfiguration will comment out lines no longer required with a # prefix. When False (the default), such lines will be deleted instead. When adding lines, regardless of this setting, the utility will search for, and uncomment, commented out lines which match the required output.
active

Returns the key of the active configuration, if any. If no configuration is currently active, returns None.

class pibootctl.store.BootConfiguration(path, config_root='config.txt', mutable_files=frozenset({'config.txt'}), comment_lines=False)[source]

Represents a boot configuration, as parsed from config_root (default “config.txt”) on the boot partition (presumably mounted at path, a Path instance).

mutable()[source]

Return a MutableConfiguration based on the parsed content of this configuration.

Note that mutable configurations are not backed by any files on disk, so nothing is actually re-written until the updated mutable configuration is assigned back to something in the Store.

config_root

The root file of the boot configuration. This is currently always “config.txt”.

files

A mapping of filenames to BootFile instances representing all the files that make up the boot configuration.

hash

The SHA1 hash that identifies the boot configuration. This is obtained by hashing the files of the boot configuration in parsing order.

path

The path (or archive or entity) containing all the files that make up the boot configuration.

settings

A Settings instance containing all the settings extracted from the boot configuration.

timestamp

The last modified timestamp of the boot configuration, as a datetime.

class pibootctl.store.StoredConfiguration(path, config_root='config.txt', mutable_files=frozenset({'config.txt'}), comment_lines=False)[source]

Represents a boot configuration stored in a ZipFile specified by path. The starting file of the configuration is given by config_root. All other parameters are as in BootConfiguration.

class pibootctl.store.MutableConfiguration(path, config_root='config.txt', mutable_files=frozenset({'config.txt'}), comment_lines=False)[source]

Represents a changeable boot configuration.

Do not construct instances of this class directly; they are typically constructed from a base BootConfiguration, by calling mutable().

Mutable configurations can be changed with the update() method which will also validate the new configuration, and check that the settings were not overridden by later files. No link is maintained between the original BootConfiguration and the mutable copy. This implies that nothing is re-written on disk when the mutable configuration is updated. The resulting configuration must be assigned back to something in the Store in order to re-write disk files.

update(values, context)[source]

Given a mapping of setting names to new values, updates the values of the corresponding settings in this configuration. If a value is None, the setting is reset to its default value.

class pibootctl.store.Settings(items=None)[source]

Represents all settings in a boot configuration; acts like an ordered mapping of names to Setting objects.

copy()[source]

Returns a distinct copy of the configuration that can be updated without affecting the original.

diff(other)[source]

Returns a set of (self, other) setting tuples for all settings that differ between self and other (another Settings instance). If a particular setting is missing from either side, its entry will be given as None.

filter(pattern)[source]

Returns a copy of the configuration which only contains settings with names matching pattern, which may contain regular shell globbing patterns.

modified()[source]

Returns a copy of the configuration which only contains modified settings.

pibootctl.term

The pibootctl.term module contains various utilities for determining the type of terminal the script is running under (term_is_dumb(), term_is_utf8(), and term_size()), for directing terminal output through the system’s pager(), and for constructing an overall ErrorHandler for the script.

class pibootctl.term.ErrorHandler[source]

Global configurable application exception handler. For “basic” errors (I/O errors, keyboard interrupt, etc.) just the error message is printed as there’s generally no need to confuse the user with a complete stack trace when it’s just a missing file. Other exceptions, however, are logged with the usual full stack trace.

The configuration can be augmented with other exception classes that should be handled specially by treating the instance as a dictionary mapping exception classes to ErrorAction tuples (or any 2-tuple, which will be converted to an ErrorAction).

For example:

>>> from pibootctl.term import ErrorAction, ErrorHandler
>>> import sys
>>> sys.excepthook = ErrorHandler()
>>> sys.excepthook[KeyboardInterrupt]
(None, 1)
>>> sys.excepthook[SystemExit]
(None, <function ErrorHandler.exc_value at 0x7f6178915e18>)
>>> sys.excepthook[ValueError] = (sys.excepthook.exc_message, 3)
>>> sys.excepthook[Exception] = ("An error occurred", 1)
>>> raise ValueError("foo is not an integer")
foo is not an integer

Note the lack of a traceback in the output; if the example were a script it would also have exited with return code 3.

clear()[source]

Remove all pre-defined error handlers.

static exc_message(exc_type, exc_value, exc_tb)[source]

Extracts the message associated with the exception (by calling str on the exception instance). The result is returned as a one-element list containing the message.

static exc_value(exc_type, exc_value, exc_tb)[source]

Returns the first argument of the exception instance. In the case of SystemExit this is the expected return code of the script.

static syntax_error(exc_type, exc_value, exc_tb)[source]

Returns the message associated with the exception, and an additional line suggested the user try the --help option. This should be used in response to exceptions indicating the user made an error in their command line.

class pibootctl.term.ErrorAction(message, exitcode)[source]

Named tuple dictating the action to take in response to an unhandled exception of the type it is associated with in ErrorHandler. The message is an iterable of lines to be output as critical error log messages, and exitcode is an integer to return as the exit code of the process.

Either of these can also be functions which will be called with the exception info (type, value, traceback) and will be expected to return an iterable of lines (for message) or an integer (for exitcode).

pibootctl.term.term_is_dumb()[source]

Returns True if stdout is something other than a TTY (e.g. a file redirection or a pipe).

pibootctl.term.term_is_utf8()[source]

Returns True if the code-set of the current locale is ‘UTF-8’.

pibootctl.term.term_size()[source]

Returns the size of the console as a (rows, cols) tuple.

pibootctl.term.pager(enable=None)[source]

Used as a context manager to redirect stdout to the system’s pager utility (“pager”, “less”, or “more” are all attempted, in that order).

By default (when enable is None), stdout will only be redirected if stdout is connected to a TTY. If enable is True stdout will always be redirected, and likewise when enable is False the function will do nothing.

For example, the following script should print “Hello, world!”, piping the result through the system’s pager:

from pibootctl.term import pager
with pager():
    print("Hello, world!")

pibootctl.userstr

The pibootctl.userstr module provides the UserStr class which represents unparsed user input on the command line.

The module also provides a variety of functions for converting input (either from JSON, YAML, or other structured formats, or from unparsed UserStr) into common types (to_bool(), to_int(), to_str(), etc).

class pibootctl.userstr.UserStr[source]

Type used to represent a value expressed as a string on the command line. In other words, any value bearing this type is a string representation of some other type (possibly str, int, None, etc.)

Primarily used by various conversion routines (to_bool(), to_str(), etc.) to determine whether a value is a string parsed from some serialization format (like JSON or YAML) which should be treated as a string literal.

Note

The blank UserStr is special in that it always represents None in conversions.

pibootctl.userstr.to_bool(s)[source]

Converts the UserStr (or other type) s to a bool. Various “typical” string representations of true and false are accepted including “true”, “yes”, and “on”, along with their counter-parts “false”, “no”, and “off”. Literal None passes through unchanged, and a blank UserStr will convert to None.

pibootctl.userstr.to_int(s)[source]

Converts the UserStr (or other type) s to a int. As with all UserStr conversions, blank string inputs are converted to None, and literal None passes through unchanged. Otherwise, decimal integers and hexi-decimal integers prefixed with “0x” are accepted.

pibootctl.userstr.to_float(s)[source]

Converts the UserStr (or other type) s to a float. As with all UserStr conversions, blank string inputs are converted to None, and literal None passes through unchanged. Otherwise, typical floating point values (optionally prefixed with sign, optionally suffixed with an exponent) are accepted.

pibootctl.userstr.to_str(s)[source]

Converts the UserStr (or other type) s to a str. Blank UserStr are converted to None, and literal None passes through unchanged. Everything else is simply passed to the str constructor.

pibootctl.userstr.to_list(s, sep=', ')[source]

Converts the UserStr (or other type) s to a list based on the separator character sep (which defaults to “,”). Blank UserStr are converted to None, and literal None passes through unchanged. Everything else is passed to the list constructor. This ensures that the result is always a unique reference.

Changelog

Release 0.5.2 (2020-09-14)

  • Fix handling of initramfs (ramfsaddr=0 doesn’t work)

Release 0.5.1 (2020-09-09)

  • Handle future model numbers elegantly
  • Rewrote the configuration setting code to always target config.txt as several settings don’t work in included files (e.g. start_x).
  • Added comment_lines configuration option to permit commenting out lines instead of deleting them
  • Enhanced the configuration setting code to search for and uncomment existing lines in preference to writing new ones
  • Added --this-model and --this-serial options to permit adding settings in new conditional sections

Release 0.4 (2020-03-31)

  • Handle unrecognized commands correctly in the “help” command
  • Implemented loading settings with the --shell style
  • Improved help output for reference lists
  • Fixed all legal stuff (added copyright headers where required, re-licensed to GPL 3+)

Release 0.3 (2020-03-27)

  • Added full bash completion support

Release 0.2 (2020-03-26)

  • The application now reports which lines overrode a setting when the “ineffective setting” error occurs
  • Added the max_framebuffers setting, and detection for the vc4-*-v3d overlays
  • Fixed restoring the default configuration in which config.txt doesn’t exist (i.e. when config.txt should be deleted or blanked; the prior version simply left the old config.txt in place incorrectly)
  • Various documentation fixes

Release 0.1.1 (2020-03-13)

  • Fixed broken build on Bionic

Release 0.1 (2020-03-13)

  • Initial release.
  • Please note that as this is a pre-v1 release, API stability is not yet guaranteed.

License

This file is part of pibootctl.

pibootctl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

pibootctl is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with pibootctl. If not, see <https://www.gnu.org/licenses/>.