This document is intended for those who want to create new extensions for RedPill Load. Before you start you should read the general overview document first.
- Anatom of an extension
- Index file
- Recipe file
- [Scripts environment]
- Example extension
- Why packages at all?
The most basic extension consists of three files:
- Index file
- Recipe file
- File you're delivering to the target system
See below for details regarding these files and rationale behind them
The index file is the heart of every extension and lists information about the extension itself. It does not contain any details of what the extension does, it only says what the extension is. See the sample JSON below with all possible options and comments below it:
{
"id": "developer_name.sample_name",
"url": "http://raw.githubusercontent.com/amazing-dev/sample-name/master/rpext-sample_name.json",
"info": {
"name": "Sample Extension",
"description": "Sample description for the sample extension",
"author_url": "https://anotherexample.tld/about_sample.html",
"packer_url": "https://github.com/amazing-dev/sample-name",
"help_url": "https://example.tld/forum/amazing-sample-extension.html"
},
"releases": {
"ds3615xs_25556": "http://raw.githubusercontent.com/amazing-dev/sample-name/master/recipes/v6.json",
"ds3615xs_41222": "http://raw.githubusercontent.com/amazing-dev/sample-name/master/recipes/v7.json",
"ds918p_25556": "http://raw.githubusercontent.com/amazing-dev/sample-name/master/recipes/v6.json",
"ds918p_41890": "http://raw.githubusercontent.com/amazing-dev/sample-name/master/recipes/v7.json"
}
}
Below are description of fields & recommendations. All fields marked with *
are required.
id
*: unique ID of the extension. The name MUST NOT contain spaces. In general, we limit the name to contain ASCII letters (A-Z
,a-z
), numbers (0-9
), underscores (_
), dashes (-
), and periods/dots (.
). Theid
must start with a letter or number. We recommend the ID to be in a formatdeveloper_name.extension_name
to avoid conflicts.url
*: URL to the JSON index file (the one you're creating). This URL is used to check the updates for the index file itself. It will be queried every time an image is built by anyone. We recommend using GitHub raw URLs, as it's a perfect static hosting for text files which aren't very large.info
*: section containing all user-readable informationname
*: human-readable name of the extension. Unlikeid
this one can contain any characters. While we don't limit what can be put here you should probably stay away from new lines or non-ASCII characters (as not all terminals can display them) unless your extension is only usable by non-English speaking users.description
: human-readable short description of what the extension is for. The same character constrains apply here as inname
. This field is optionalauthor_url
: URL for the author of the extension itself (i.e. code). This is presented to users as the place to check for help with e.g. supported hardware etc. This field is optionalpacker_url
: URL to direct users to any packer (i.e. person who prepared the extension for RedPill) website/page. Usually we expect that URL to point to a GitHub project where packers can share all the details needed. This field is optionalhelp_url
*: A required field containing a URL to a place where users can get help with the extension. In some cases we can detect that problem arose because of the extension being broken (e.g. broken recipe file) and not because the extension manager problem. In such cases we instruct users to go there instead of creating an issue in the manager repository. This URL should lead to either GitHub issues page or some forum thread.
releases
*: List of platforms supported by the extension- Every key specifies platform code. This code is the same as used by the files in
config/<HW>/<OS>/config.json
files. This is how theredpill-load
knows which version to pick for a given loader image being built - Every value is a URL to so-called recipe file. The recipe file describes what the extension does and how it should do things. See section below for details.
- Each release can have a unique URL, or the URL can be shared between different releases/platforms. In case of drivers (i.e. kernel modules) you have no choice other than having unique recipes per platform as kernel modules are unique per kernel version. However, if multiple software versions share the same kernel you can sometimes get away with using the same binary extensions. However, keep in mind even if the kernel VERSION didn't change it DOES NOT mean that syno didn't change the kernel (yup, that's stupid, we know). The only way is to look at the compilation date.
- For extensions containing no kernel modules you most likely will use one recipe or one recipe per major version ( like in the example above). This is useful for e.g. an extension which change CPU governor (power management).
- We recommend hosting these files on GitHub raw, as these are small text files. They will be downloaded every time someone builds a loader image for a given platform.
- Every key specifies platform code. This code is the same as used by the files in
Recipe file (listed under releases
in the index file; see section above) define what the extension does and how it
should be done. As with cooking, recipes are the heart of the extension listing all ingredients and setting the rules
of how things are loaded. See the sample JSON below with all possible options and comments below it:
{
"ext_version": "v1",
"files": [
{
"name": "check-hardware.sh",
"url": "http://raw.githubusercontent.com/amazing-dev/sample-name/master/scripts/check-hardware.sh",
"sha256": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
"packed": false
},
{
"name": "kernel-modules.tgz",
"url": "https://github.com/amazing-dev/sample-name/releases/download/v1/sample_name-kmod-3.10.105.tgz",
"sha256": "beefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead",
"packed": true
}
],
"kmods": {
"base.ko": "",
"some_other.ko": "version=1 do_a_backflip=yes"
},
"scripts": {
"on_boot": "set-cpu-speed.sh",
"check_kmod": "check-hardware.sh",
"on_os_load": "disable-syno-cpu.sh"
}
}
Below are description of fields & recommendations. All fields marked with *
are required.
ext_version
: optional version information about the extension. You can put anything here (or not have it at all)files
*: list all your files & collections of files packed into the loader image- this array can contain as many files as you want, but it's a good practice to pack large sets of files (see below)
- all fields (
name
,url
,sha256
, andpacked
) are required - entries of packed and non-packed files can be mixed
- flat files (
"packed": false
)- they're simply downloaded from the
url
given and copied into the loader image under name specified inname
- your
name
must be unique across all the files you specified (including these unpacked from all packed ones). All file names MUST only contain contain ASCII letters (A-Z
,a-z
), numbers (0-9
), underscores (_
), dashes (-
), and periods/dots (.
). If you use use a space anywhere things will break really badly due to simplifications we had to take in the code executed in the pre-boot environment (more on that in scripts section ). sha256
is mandatory to ensure we didn't accidentally get a wrong file because e.g. github broke and served "404 Not Found" withHTTP/200
code (as we saw before)
- they're simply downloaded from the
- packed archives (
"packed": true
)name
here doesn't really matter, it can be anything sensible (as it will be unpacked). However, files inside of the archive MUST be named with the same constrains as for "flat files" (see above)- if you have multiple binary files you can pack them into a single archive (e.g. what we do with VirtIO extension which has many kernel extensions). Such archive will be downloaded and unpacked to your extensions' directory in the loader image. The archive will be unpacked without folder structure for security (and consistency) reasons. If you pack your archive with folders they will be ignored and all files from them will unpack to a single directory anyway. This is by design.
- Be mindful of the size: the loader image isn't big
- The archive can be anything what
tar -x
can unpack. To be most compatible you should probably stick totar.gz
sha256
is mandatory so we can detect corruptions. This also serves as an additional feature: if you're simply replacing the file without changing the URL you will replace thesha256
and thus extension manager will know to re-download the file as the recipe file changed.
url
should be any http(s) source. You can use anything curl understands in general. If you are hosting a small text file (e.g. a script shared between different recipes) you can use GitHub raw URLs. However, if you're downloading binary files and/or something larger (like >few KB, especially packed archives) do not use these and use GitHub releases feature instead. GitHub is known to simply block repos which start hosting binary files in repos and offer downloads for them (as this puts annoying load on their services as git itself was never designed for that).
kmods
: optional section listing kernel modules which should be loaded by the RedPill upon system boot- All kernel modules will be loaded in the pre-boot environment, so that you can include drivers e.g. for LAN card
- Each key is the name of the
.ko
kernel module file. Modules are loaded in that exact order. - Each value is a string of kernel module arguments. Most modules don't need that but some require it.
- This section is optional since the extension may not deliver any kernel modules but just scripts.
- Before the first kernel module from the list is loaded the script specified in
check_kmod
is executed. This is useful if the extension is only useful for most hardware platforms but not all (e.g. our VirtIO module).
scripts
defines all scripts- All keys in this section are pre-defined script types (currently all unknown ones are simply ignored)
- All values are script file names which should exist within
files
download (either flat/packed: false
or unpacked from archives) - The whole
scripts
section is optional, as an extension can deliver only kernel modules - See next section for details about scripts executeion environment
- The following types/keys are defined:
on_boot
: executed as soon as the system boots in the preboot environment and hardware is ready. No drives are mounted at this stage but all kernel modules have been loaded. If extensions kernel modules loading failson_boot
scripts will not be executed.check_kmod
: executed before kernel modules for your extension are loaded. If your script returns with exit code of0
kernel modules defined inkmods
will be loaded. When it returns anything else (e.g.1
) loading of modules defined inkmods
will be skipped. We use this feature in VirtIO to check if the system we're running has VirtIO hardware.on_os_load
: executed after the system disk is mounted, just before starting the full OS boot. This script will NOT execute if the system is not installed.
Scripts defined here are executed by a basic busybox POSIX shell, so the syntax is very basic and limited (e.g. we
painfully miss arrays). You should use the standard #!/bin/sh
shebang for most portability. Your script will
be run in a separate shell instance and within the extension directory. All scripts are run from a read-only partition.
You should not try to circumvent that as the loader may be e.g. network booting. In practice it's usually a ramdisk.
Any script will be passed two variables:
PLATFORM_ID
: id of the platform the script is running for (e.g.ds3615xs_25556
)EXT_NAME
: extension ID (e.g.developer_name.sample_name
), useful when you're reusing the same script across many extensions
As we believe in eating or own dog food we moved the, previously hardcoded, VirtIO module to an extension. See the GitHub project for details: https://github.com/RedPill-TTG/redpill-virtio
In addition, we also published a simple script-only extension which forces the system to wait for /dev/synoboot
to be
ready before continuing the boot process. For details why see https://github.com/RedPill-TTG/redpill-boot-wait.
Previously used loader by the community (Jun's loader) used a system of a single ramdisk-like extra.lzma
archive. This
method, while worked, lacked flexibility and required hand-assembly by the packers. As we observed this lead to multiple
versions of the whole loader image floating around - some with VirtIO, some with ethernet drivers, some with SAS HBA
drivers, some with VirtIO and SAS HBA... Additionally, while reporting problems on the forum people usually reported
whether they "used extra.lzma" which without the context was limited in information. In addition, to add extra
functionality beyond kernel modules, a custom hacks had to be developed.
The extension management shipping with the redpill-load
hopes to alleviate the pain of packing and managing all
extensions and mods. We set the following objectives while developing the current version:
- as simple and as automated as possible for users
- in practice users need only a single
add
command and a URL to add an extension - we're planning a better discoverability with a list of all known extensions
- updates are checked any time a user builds a new image
- updates are offered not only on per-platform basis but extension can be updated for the currently existing platform
- if no interdependent extensions are installed users don't need to do a thing as by default all added extensions are added to the image
- in practice users need only a single
- easy for developers/packers
- the most basic extension requires 3 files: index file, recipe file, and the file which is delivered
- JSON configs were organized such that it's easy to maintain a repo with multiple versions of them (more on that above in rationales sections)
- the structure is designed for maximum reusability and easy
- error-proof
- most errors are meant to be self-fixable (even if user caused them)
- when an error is displayed it tries to offer a reason and a possible way to fix it (like in
git
) - all extensions offer multiple links pointing users to the correct place (do they have a problem with USING the extension? or maybe they have a problem with it being unstable? or maybe they want to see the forum thread where they can ask the author questions? => it's all supported)
- the code will prevent users from updating if extensions they rely on are not updated (as what's the point of updating to a new OS release if your ethernet driver doesn't work yet?)
- flexible in nature
- at first, we jotted the idea as delivering kernel extensions/drivers, but we quickly realized it's not enough
- the current (early) version allows for pretty much any customization of the OS before it's booted with custom scripts
- current implementation is more a PoC (that's why it's a messy shell script monstrosity), but with feedback can be eventually built to a much higher standard
- decentralized
- we don't want to exert any control or force anyone to do anything: this is our goal from the beginning
- the extension manager is designed to be not only open-source but open in nature. We deliberately don't offer any centralized repository and simply rely on community and the trust within it to offer a quality contribution