Magisk/docs/guides.md

307 lines
16 KiB
Markdown
Raw Permalink Normal View History

2018-10-15 22:04:51 +00:00
# Developer Guides
2018-10-15 08:33:42 +00:00
2020-10-03 04:47:47 +00:00
## BusyBox
2022-01-25 10:32:52 +00:00
Magisk ships with a feature complete BusyBox binary (including full SELinux support). The executable is located at `/data/adb/magisk/busybox`. Magisk's BusyBox supports runtime toggle-able "ASH Standalone Shell Mode". What this standalone mode means is that when running in the `ash` shell of BusyBox, every single command will directly use the applet within BusyBox, regardless of what is set as `PATH`. For example, commands like `ls`, `rm`, `chmod` will **NOT** use what is in `PATH` (in the case of Android by default it will be `/system/bin/ls`, `/system/bin/rm`, and `/system/bin/chmod` respectively), but will instead directly call internal BusyBox applets. This makes sure that scripts always run in a predictable environment and always have the full suite of commands no matter which Android version it is running on. To force a command _not_ to use BusyBox, you have to call the executable with full paths.
2020-10-03 04:47:47 +00:00
Every single shell script running in the context of Magisk will be executed in BusyBox's `ash` shell with standalone mode enabled. For what is relevant to 3rd party developers, this includes all boot scripts and module installation scripts.
For those who want to use this "Standalone Mode" feature outside of Magisk, there are 2 ways to enable it:
1. Set environment variable `ASH_STANDALONE` to `1`<br>Example: `ASH_STANDALONE=1 /data/adb/magisk/busybox sh <script>`
2. Toggle with command-line options:<br>`/data/adb/magisk/busybox sh -o standalone <script>`
2021-02-24 07:56:58 +00:00
To make sure all subsequent `sh` shell executed also runs in standalone mode, option 1 is the preferred method (and this is what Magisk and the Magisk app internally use) as environment variables are inherited down to child processes.
2020-10-03 04:47:47 +00:00
2018-10-15 08:33:42 +00:00
## Magisk Modules
2022-01-25 10:32:52 +00:00
2019-12-28 19:28:03 +00:00
A Magisk module is a folder placed in `/data/adb/modules` with the structure below:
2018-10-15 08:33:42 +00:00
```
2019-03-28 08:48:46 +00:00
/data/adb/modules
2018-10-15 08:33:42 +00:00
├── .
├── .
|
├── $MODID <--- The folder is named with the ID of the module
│ │
│ │ *** Module Identity ***
│ │
2022-01-25 10:32:52 +00:00
│ ├── module.prop <--- This file stores the metadata of the module
2019-12-28 19:28:03 +00:00
│ │
│ │ *** Main Contents ***
│ │
2022-01-25 10:32:52 +00:00
│ ├── system <--- This folder will be mounted if skip_mount does not exist
│ │ ├── ...
│ │ ├── ...
│ │ └── ...
│ │
2022-01-25 11:04:23 +00:00
│ ├── zygisk <--- This folder contains the module's Zygisk native libraries
2022-01-25 10:32:52 +00:00
│ │ ├── arm64-v8a.so
│ │ ├── armeabi-v7a.so
2024-07-02 21:53:46 +00:00
│ │ ├── riscv64.so
2022-01-25 10:32:52 +00:00
│ │ ├── x86.so
│ │ ├── x86_64.so
│ │ └── unloaded <--- If exists, the native libraries are incompatible
2018-10-15 08:33:42 +00:00
│ │
2019-12-28 19:28:03 +00:00
│ │ *** Status Flags ***
2018-10-15 08:33:42 +00:00
│ │
2022-01-25 10:32:52 +00:00
│ ├── skip_mount <--- If exists, Magisk will NOT mount your system folder
│ ├── disable <--- If exists, the module will be disabled
│ ├── remove <--- If exists, the module will be removed next reboot
2018-10-15 08:33:42 +00:00
│ │
2019-12-28 19:28:03 +00:00
│ │ *** Optional Files ***
2018-10-15 08:33:42 +00:00
│ │
2022-01-25 10:32:52 +00:00
│ ├── post-fs-data.sh <--- This script will be executed in post-fs-data
│ ├── service.sh <--- This script will be executed in late_start service
2019-03-28 08:48:46 +00:00
| ├── uninstall.sh <--- This script will be executed when Magisk removes your module
| ├── action.sh <--- This script will be executed when user click the action button in Magisk app
2022-01-25 10:32:52 +00:00
│ ├── system.prop <--- Properties in this file will be loaded as system properties by resetprop
│ ├── sepolicy.rule <--- Additional custom sepolicy rules
│ │
2019-12-28 19:28:03 +00:00
│ │ *** Auto Generated, DO NOT MANUALLY CREATE OR MODIFY ***
2022-01-25 10:32:52 +00:00
│ │
│ ├── vendor <--- A symlink to $MODID/system/vendor
│ ├── product <--- A symlink to $MODID/system/product
│ ├── system_ext <--- A symlink to $MODID/system/system_ext
2018-10-15 08:33:42 +00:00
│ │
2019-12-28 19:28:03 +00:00
│ │ *** Any additional files / folders are allowed ***
2018-10-15 08:33:42 +00:00
│ │
2022-01-25 10:32:52 +00:00
│ ├── ...
│ └── ...
2018-10-15 08:33:42 +00:00
|
├── another_module
2022-01-25 10:32:52 +00:00
│ ├── .
│ └── .
2018-10-15 08:33:42 +00:00
├── .
├── .
```
2019-12-28 19:28:03 +00:00
#### module.prop
This is the **strict** format of `module.prop`
2018-10-15 08:33:42 +00:00
```
id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>
2022-01-25 10:32:52 +00:00
updateJson=<url> (optional)
2018-10-15 08:33:42 +00:00
```
2022-01-25 10:32:52 +00:00
2019-03-28 08:48:46 +00:00
- `id` has to match this regular expression: `^[a-zA-Z][a-zA-Z0-9._-]+$`<br>
2022-01-25 10:32:52 +00:00
ex: ✓ `a_module`, ✓ `a.module`, ✓ `module-101`, ✗ `a module`, ✗ `1_module`, ✗ `-a-module`<br>
This is the **unique identifier** of your module. You should not change it once published.
2018-10-15 08:33:42 +00:00
- `versionCode` has to be an **integer**. This is used to compare versions
2022-01-25 10:32:52 +00:00
- `updateJson` should point to a URL that downloads a JSON to provide info so the Magisk app can update the module.
2022-01-26 07:04:57 +00:00
- Others that weren't mentioned above can be any **single line** string.
- Make sure to use the `UNIX (LF)` line break type and not the `Windows (CR+LF)` or `Macintosh (CR)`.
2022-01-25 10:32:52 +00:00
Update JSON format:
```
{
"version": string,
"versionCode": int,
"zipUrl": url,
"changelog": url
}
```
2019-12-28 19:28:03 +00:00
#### Shell scripts (`*.sh`)
2022-01-25 10:32:52 +00:00
Please read the [Boot Scripts](#boot-scripts) section to understand the difference between `post-fs-data.sh` and `service.sh`. For most module developers, `service.sh` should be good enough if you just need to run a boot script. If you need to wait for boot completed, you can use `resetprop -w sys.boot_completed 0`.
2019-12-28 19:28:03 +00:00
In all scripts of your module, please use `MODDIR=${0%/*}` to get your module's base directory path; do **NOT** hardcode your module path in scripts.
2022-01-25 10:32:52 +00:00
If Zygisk is enabled, the environment variable `ZYGISK_ENABLED` will be set to `1`.
2019-12-28 19:28:03 +00:00
2022-01-25 11:04:23 +00:00
#### The `system` folder
2022-01-26 07:04:57 +00:00
All files you want to replace/inject should be placed in this folder. This folder will be recursively merged into the real `/system`; that is: existing files in the real `/system` will be replaced by the one in the module's `system`, and new files in the module's `system` will be added to the real `/system`.
2022-01-25 11:04:23 +00:00
2022-01-26 07:04:57 +00:00
If you place a file named `.replace` in any of the folders, instead of merging its contents, that folder will directly replace the one in the real system. This can be very handy for swapping out an entire folder.
2022-01-25 11:04:23 +00:00
If you want to replace files in `/vendor`, `/product`, or `/system_ext`, please place them under `system/vendor`, `system/product`, and `system/system_ext` respectively. Magisk will transparently handle whether these partitions are in a separate partition or not.
2022-01-25 11:04:23 +00:00
#### Zygisk
2022-01-26 07:04:57 +00:00
Zygisk is a feature of Magisk that allows advanced module developers to run code directly in every Android applications' processes before they are specialized and running. For more details about the Zygisk API and building a Zygisk module, please checkout the [Zygisk Module Sample](https://github.com/topjohnwu/zygisk-module-sample) project.
2022-01-25 11:04:23 +00:00
2019-12-28 19:28:03 +00:00
#### system.prop
2022-01-25 10:32:52 +00:00
2019-12-28 19:28:03 +00:00
This file follows the same format as `build.prop`. Each line comprises of `[key]=[value]`.
#### sepolicy.rule
2022-01-25 10:32:52 +00:00
2022-01-26 07:04:57 +00:00
If your module requires some additional sepolicy patches, please add those rules into this file. Each line in this file will be treated as a policy statement. For more details about how a policy statement is formatted, please check [magiskpolicy](tools.md#magiskpolicy)'s documentation.
2019-12-28 19:28:03 +00:00
2019-03-28 08:48:46 +00:00
## Magisk Module Installer
2018-10-15 08:33:42 +00:00
2022-01-26 07:04:57 +00:00
A Magisk module installer is a Magisk module packaged in a zip file that can be flashed in the Magisk app or custom recoveries such as TWRP. The simplest Magisk module installer is just a Magisk module packed as a zip file, in addition to the following files:
2019-12-28 19:28:03 +00:00
- `update-binary`: Download the latest [module_installer.sh](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh) and rename/copy that script as `update-binary`
- `updater-script`: This file should only contain the string `#MAGISK`
2022-01-26 07:04:57 +00:00
The module installer script will setup the environment, extract the module files from the zip file to the correct location, then finalizes the installation process, which should be good enough for most simple Magisk modules.
2019-12-28 19:28:03 +00:00
```
module.zip
├── META-INF
2022-01-25 10:32:52 +00:00
│ └── com
│ └── google
│ └── android
│ ├── update-binary <--- The module_installer.sh you downloaded
│ └── updater-script <--- Should only contain the string "#MAGISK"
2019-12-28 19:28:03 +00:00
├── customize.sh <--- (Optional, more details later)
│ This script will be sourced by update-binary
├── ...
├── ... /* The rest of module's files */
2020-10-03 04:47:47 +00:00
2019-12-28 19:28:03 +00:00
```
#### Customization
2022-01-26 07:04:57 +00:00
If you need to customize the module installation process, optionally you can create a script in the installer named `customize.sh`. This script will be _sourced_ (not executed!) by the module installer script after all files are extracted and default permissions and secontext are applied. This is very useful if your module require additional setup based on the device ABI, or you need to set special permissions/secontext for some of your module files.
2018-10-15 08:33:42 +00:00
2022-01-26 07:04:57 +00:00
If you would like to fully control and customize the installation process, declare `SKIPUNZIP=1` in `customize.sh` to skip all default installation steps. By doing so, your `customize.sh` will be responsible to install everything by itself.
2019-12-28 19:28:03 +00:00
2022-01-26 07:04:57 +00:00
The `customize.sh` script runs in Magisk's BusyBox `ash` shell with "Standalone Mode" enabled. The following variables and functions are available:
2019-12-28 19:28:03 +00:00
##### Variables
2022-01-25 10:32:52 +00:00
2019-12-28 19:28:03 +00:00
- `MAGISK_VER` (string): the version string of current installed Magisk (e.g. `v20.0`)
- `MAGISK_VER_CODE` (int): the version code of current installed Magisk (e.g. `20000`)
2021-02-24 07:56:58 +00:00
- `BOOTMODE` (bool): `true` if the module is being installed in the Magisk app
2019-12-28 19:28:03 +00:00
- `MODPATH` (path): the path where your module files should be installed
- `TMPDIR` (path): a place where you can temporarily store files
- `ZIPFILE` (path): your module's installation zip
2024-07-02 21:53:46 +00:00
- `ARCH` (string): the CPU architecture of the device. Value is either `arm`, `arm64`, `x86`, `x64`, or `riscv64`
- `IS64BIT` (bool): `true` if `$ARCH` is either `arm64`, `x64`, or `riscv64`
2023-03-17 12:08:31 +00:00
- `API` (int): the API level (Android version) of the device (e.g. `23` for Android 6.0)
2019-12-28 19:28:03 +00:00
##### Functions
```
ui_print <msg>
2023-05-13 09:37:52 +00:00
Print <msg> to console
2019-12-28 19:28:03 +00:00
Avoid using 'echo' as it will not display in custom recovery's console
abort <msg>
2023-05-13 09:37:52 +00:00
Print error message <msg> to console and terminate the installation
2019-12-28 19:28:03 +00:00
Avoid using 'exit' as it will skip the termination cleanup steps
set_perm <target> <owner> <group> <permission> [context]
2023-05-13 09:37:52 +00:00
If [context] is not specified, the default is "u:object_r:system_file:s0"
This function is a shorthand for the following commands:
2019-12-28 19:28:03 +00:00
chown owner.group target
chmod permission target
chcon context target
set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
2023-05-13 09:37:52 +00:00
If [context] is not specified, the default is "u:object_r:system_file:s0"
This function is a shorthand for the following psuedo code:
set_perm <directory> owner group dirpermission context
for file in <directory>:
set_perm file owner group filepermission context
for dir in <directory>:
set_perm_recursive dir owner group dirpermission context
2019-12-28 19:28:03 +00:00
```
2022-01-26 07:04:57 +00:00
For convenience, you can also declare a list of folders you want to replace in the variable name `REPLACE`. The module installer script will create the `.replace` file into the folders listed in `REPLACE`. For example:
2019-12-28 19:28:03 +00:00
```
REPLACE="
/system/app/YouTube
/system/app/Bloatware
"
```
2022-01-25 10:32:52 +00:00
2022-01-26 07:04:57 +00:00
The list above will result in the following files being created: `$MODPATH/system/app/YouTube/.replace` and `$MODPATH/system/app/Bloatware/.replace`.
2019-12-28 19:28:03 +00:00
#### Notes
2022-01-26 07:04:57 +00:00
- When your module is downloaded with the Magisk app, `update-binary` will be **forcefully** replaced with the latest [`module_installer.sh`](https://github.com/topjohnwu/Magisk/blob/master/scripts/module_installer.sh). **DO NOT** try to add any custom logic in `update-binary`.
- Due to historical reasons, **DO NOT** add a file named `install.sh` in your module installer zip.
- **DO NOT** call `exit` at the end of `customize.sh`. The module installer script has to perform some cleanups before exiting.
2018-10-15 08:33:42 +00:00
2019-03-28 08:48:46 +00:00
## Boot Scripts
2019-12-28 19:28:03 +00:00
2019-03-28 08:48:46 +00:00
In Magisk, you can run boot scripts in 2 different modes: **post-fs-data** and **late_start service** mode.
- post-fs-data mode
- This stage is BLOCKING. The boot process is paused before execution is done, or 40 seconds have passed.
2022-01-25 10:32:52 +00:00
- Scripts run before any modules are mounted. This allows a module developer to dynamically adjust their modules before it gets mounted.
- This stage happens before Zygote is started, which pretty much means everything in Android
2022-01-25 11:52:46 +00:00
- **WARNING:** using `setprop` will deadlock the boot process! Please use `resetprop -n <prop_name> <prop_value>` instead.
2022-01-26 07:04:57 +00:00
- **Only run scripts in this mode if necessary.**
2019-03-28 08:48:46 +00:00
- late_start service mode
2022-01-26 07:04:57 +00:00
- This stage is NON-BLOCKING. Your script runs in parallel with the rest of the booting process.
- **This is the recommended stage to run most scripts.**
2019-03-28 08:48:46 +00:00
In Magisk, there are also 2 kinds of scripts: **general scripts** and **module scripts**.
- General Scripts
2022-01-25 10:32:52 +00:00
- Placed in `/data/adb/post-fs-data.d` or `/data/adb/service.d`
2022-01-26 07:04:57 +00:00
- Only executed if the script is set as executable (`chmod +x script.sh`)
2022-01-25 10:32:52 +00:00
- Scripts in `post-fs-data.d` runs in post-fs-data mode, and scripts in `service.d` runs in late_start service mode.
2022-01-26 07:04:57 +00:00
- Modules should **NOT** add general scripts during installation
2019-03-28 08:48:46 +00:00
- Module Scripts
2022-01-26 07:04:57 +00:00
- Placed in the module's own folder
2022-01-25 10:32:52 +00:00
- Only executed if the module is enabled
- `post-fs-data.sh` runs in post-fs-data mode, and `service.sh` runs in late_start service mode.
2018-10-15 08:33:42 +00:00
2022-01-26 07:04:57 +00:00
All boot scripts will run in Magisk's BusyBox `ash` shell with "Standalone Mode" enabled.
2018-10-15 08:33:42 +00:00
2019-09-19 09:00:29 +00:00
## Root Directory Overlay System
2019-12-28 19:28:03 +00:00
2020-10-03 04:47:47 +00:00
Since `/` is read-only on system-as-root devices, Magisk provides an overlay system to enable developers to replace files in rootdir or add new `*.rc` scripts. This feature is designed mostly for custom kernel developers.
Overlay files shall be placed in the `overlay.d` folder in boot image ramdisk, and they follow these rules:
1. Each `*.rc` file (except for `init.rc`) in `overlay.d` will be read and concatenated **AFTER** `init.rc` if it does not exist in the root directory, otherwise it will **REPLACE** the existing one.
2020-10-03 04:47:47 +00:00
2. Existing files can be replaced by files located at the same relative path
3. Files that correspond to a non-existing file will be ignored
2022-01-26 07:04:57 +00:00
To add additional files which you can refer to in your custom `*.rc` scripts, add them into `overlay.d/sbin`. The 3 rules above do not apply to anything in this folder; instead, they will be directly copied to Magisk's internal `tmpfs` directory (which used to always be `/sbin`).
2020-10-03 04:47:47 +00:00
Starting from Android 11, the `/sbin` folder may no longer exists, and in that scenario, Magisk uses `/debug_ramdisk` instead. Every occurrence of the pattern `${MAGISKTMP}` in your `*.rc` scripts will be replaced with the Magisk `tmpfs` folder when `magiskinit` injects it into `init.rc`. On pre Android 11 devices, `${MAGISKTMP}` will simply be replaced with `/sbin`, so **NEVER** hardcode `/sbin` in the `*.rc` scripts when referencing these additional files.
2019-09-19 09:00:29 +00:00
2022-01-26 07:04:57 +00:00
Here is an example of how to setup `overlay.d` with a custom `*.rc` script:
2020-10-03 04:47:47 +00:00
```
ramdisk
├── overlay.d
2022-01-25 10:32:52 +00:00
│ ├── sbin
2020-10-03 04:47:47 +00:00
│ │ ├── libfoo.ko <--- These 2 files will be copied
2022-01-26 07:04:57 +00:00
│ │ └── myscript.sh <--- into Magisk's tmpfs directory
2022-01-25 10:32:52 +00:00
│ ├── custom.rc <--- This file will be injected into init.rc
│ ├── res
│ │ └── random.png <--- This file will replace /res/random.png
│ └── new_file <--- This file will be ignored because
2020-10-03 04:47:47 +00:00
│ /new_file does not exist
├── res
2022-01-25 10:32:52 +00:00
│ └── random.png <--- This file will be replaced by
2020-10-03 04:47:47 +00:00
│ /overlay.d/res/random.png
├── ...
├── ... /* The rest of initramfs files */
```
Here is an example of the `custom.rc`:
```
# Use ${MAGISKTMP} to refer to Magisk's tmpfs directory
on early-init
setprop sys.example.foo bar
insmod ${MAGISKTMP}/libfoo.ko
start myservice
service myservice ${MAGISKTMP}/myscript.sh
oneshot
```