# Руководство по разработке модулей {#introduction}

KernelSU предоставляет механизм модулей, позволяющий добиться эффекта модификации системного каталога при сохранении целостности системного раздела. Этот механизм принято называть "бессистемным".

Модульный механизм KernelSU практически аналогичен механизму Magisk. Если вы знакомы с разработкой модулей Magisk, то разработка модулей KernelSU очень похожа. Представление модулей ниже можно пропустить, достаточно прочитать [различия-с-magisk] (difference-with-magisk.md).

## Busybox

В комплект поставки KernelSU входит полнофункциональный бинарный файл BusyBox (включая полную поддержку SELinux). Исполняемый файл находится по адресу `/data/adb/ksu/bin/busybox`. BusyBox от KernelSU поддерживает переключаемый во время работы "ASH Standalone Shell Mode". Этот автономный режим означает, что при запуске в оболочке `ash` BusyBox каждая команда будет напрямую использовать апплет внутри BusyBox, независимо от того, что задано в качестве `PATH`. Например, такие команды, как `ls`, `rm`, `chmod` будут **НЕ** использовать то, что находится в `PATH` (в случае Android по умолчанию это будут `/system/bin/ls`, `/system/bin/rm` и `/system/bin/chmod` соответственно), а вместо этого будут напрямую вызывать внутренние апплеты BusyBox. Это гарантирует, что скрипты всегда будут выполняться в предсказуемом окружении и всегда будут иметь полный набор команд, независимо от того, на какой версии Android они выполняются. Чтобы заставить команду _не_ использовать BusyBox, необходимо вызвать исполняемый файл с полными путями.

Каждый сценарий оболочки, запущенный в контексте KernelSU, будет выполняться в оболочке BusyBox `ash` с включенным автономным режимом. Для сторонних разработчиков это касается всех загрузочных скриптов и скриптов установки модулей.

Для тех, кто хочет использовать эту возможность "Автономного режима" вне KernelSU, есть два способа включить ее:

1. Установите переменной окружения `ASH_STANDALONE` значение `1`<br>Пример: `ASH_STANDALONE=1 /data/adb/ksu/bin/busybox sh <script>`
2. Переключитесь с помощью параметров командной строки:<br>`/data/adb/ksu/bin/busybox sh -o standalone <script>`

Чтобы убедиться, что все последующие запуски оболочки `sh` также выполняются в автономном режиме, предпочтительным методом является вариант 1 (и это то, что KernelSU и менеджер KernelSU используют внутри), поскольку переменные окружения наследуются вплоть до дочерних процессов.

::: tip отличие от Magisk

BusyBox в KernelSU теперь использует бинарный файл, скомпилированный непосредственно из проекта Magisk. **Поэтому вам не нужно беспокоиться о проблемах совместимости между скриптами BusyBox в Magisk и KernelSU, поскольку они абсолютно одинаковы!
:::

## Модули KernelSU {#kernelsu-modules}

Модуль KernelSU - это папка, размещенная в каталоге `/data/adb/modules` и имеющая следующую структуру:

```txt
/data/adb/modules
├── .
├── .
|
├── $MODID                  <--- Папка имеет имя с идентификатором модуля
│   │
│   │      *** Идентификация модуля ***
│   │
│   ├── module.prop         <--- В этом файле хранятся метаданные модуля
│   │
│   │      *** Основное содержимое ***
│   │
│   ├── system              <--- Эта папка будет смонтирована, если skip_mount не существует
│   │   ├── ...
│   │   ├── ...
│   │   └── ...
│   │
│   │      *** Флаги состояния ***
│   │
│   ├── skip_mount          <--- Если он существует, то KernelSU НЕ будет монтировать вашу системную папку
│   ├── disable             <--- Если модуль существует, то он будет отключен
│   ├── remove              <--- Если модуль существует, то при следующей перезагрузке он будет удален
│   │
│   │      *** Необязательные файлы ***
│   │
│   ├── post-fs-data.sh     <--- Этот скрипт будет выполняться в post-fs-data
│   ├── service.sh          <--- Этот скрипт будет выполняться в сервисе late_start
|   ├── uninstall.sh        <--- Этот скрипт будет выполнен, когда KernelSU удалит ваш модуль
│   ├── system.prop         <--- Свойства из этого файла будут загружены в качестве системных свойств программой resetprop
│   ├── sepolicy.rule       <--- Дополнительные пользовательские правила sepolicy
│   │
│   │      *** Автоматически генерируется, НЕЛЬЗЯ создавать или изменять вручную ***
│   │
│   ├── vendor              <--- Символьная ссылка на $MODID/system/vendor
│   ├── product             <--- Символьная ссылка на $MODID/system/product
│   ├── system_ext          <--- Симлинк на $MODID/system/system_ext
│   │
│   │      *** Допускается использование любых дополнительных файлов/папок ***
│   │
│   ├── ...
│   └── ...
|
├── another_module
│   ├── .
│   └── .
├── .
├── .
```

::: tip различия с Magisk
KernelSU не имеет встроенной поддержки Zygisk, поэтому в модуле нет содержимого, связанного с Zygisk. Однако для поддержки модулей Zygisk можно использовать [ZygiskOnKernelSU](https://github.com/Dr-TSNG/ZygiskOnKernelSU). В этом случае содержимое модуля Zygisk идентично содержимому, поддерживаемому Magisk.
:::

### module.prop

module.prop - это конфигурационный файл модуля. В KernelSU, если модуль не содержит этого файла, он не будет распознан как модуль. Формат этого файла следующий:

```txt
id=<string>
name=<string>
version=<string>
versionCode=<int>
author=<string>
description=<string>
```

- `id` должно соответствовать данному регулярному выражению: `^[a-zA-Z][a-zA-Z0-9._-]+$`<br>
  экс: ✓ `a_module`, ✓ `a.module`, ✓ `module-101`, ✗ `a module`, ✗ `1_module`, ✗ `-a-module`<br>
  Это **уникальный идентификатор** вашего модуля. Не следует изменять его после публикации.
- `versionCode` должен быть **целым**. Это используется для сравнения версий
- Другими, не упомянутыми выше, могут быть любые **однострочные** строки.
- Обязательно используйте тип перевода строки `UNIX (LF)`, а не `Windows (CR+LF)` или `Macintosh (CR)`.

### Сценарии командной оболочки {#shell-scripts}

Чтобы понять разницу между `post-fs-data.sh` и `Service.sh`, прочитайте раздел [Boot Scripts](#boot-scripts). Для большинства разработчиков модулей `service.sh` должно быть достаточно, если вам нужно просто запустить загрузочный скрипт.

Во всех скриптах вашего модуля используйте `MODDIR=${0%/*}` для получения пути к базовому каталогу вашего модуля; **НЕ** кодируйте жестко путь к вашему модулю в скриптах.

::: tip различия с Magisk
С помощью переменной окружения KSU можно определить, выполняется ли сценарий в KernelSU или в Magisk. Если скрипт выполняется в KernelSU, то это значение будет равно true.
:::

### каталог `system` {#system-directories}

После загрузки системы содержимое этого каталога будет наложено поверх раздела /system с помощью overlayfs. Это означает, что:

1. Файлы с теми же именами, что и в соответствующем каталоге в системе, будут перезаписаны файлами в этом каталоге.
2. Папки с теми же именами, что и в соответствующем каталоге в системе, будут объединены с папками в этом каталоге.

Если вы хотите удалить файл или папку в исходном каталоге системы, необходимо создать файл с тем же именем, что и файл/папка, в каталоге модуля с помощью команды `mknod filename c 0 0`. Таким образом, система overlayfs автоматически "забелит" этот файл, как если бы он был удален (раздел /system при этом фактически не изменится).

Вы также можете объявить в `customize.sh` переменную с именем `REMOVE`, содержащую список каталогов для выполнения операций удаления, и KernelSU автоматически выполнит команду `mknod <TARGET> c 0 0` в соответствующих каталогах модуля. Например:

```sh
REMOVE="
/system/app/YouTube
/system/app/Bloatware
"
```

В приведенном выше списке будут выполнены команды `mknod $MODPATH/system/app/YouTuBe c 0 0` и `mknod $MODPATH/system/app/Bloatware c 0 0`; при этом `/system/app/YouTube` и `/system/app/Bloatware` будут удалены после вступления модуля в силу.

Если вы хотите заменить каталог в системе, то необходимо создать каталог с тем же путем в каталоге модуля, а затем установить для этого каталога атрибут `setfattr -n trusted.overlay.opaque -v y <TARGET>`. Таким образом, система overlayfs автоматически заменит соответствующий каталог в системе (без изменения раздела /system).

Вы можете объявить в файле `customize.sh` переменную с именем `REPLACE`, содержащую список заменяемых каталогов, и KernelSU автоматически выполнит соответствующие операции в каталоге вашего модуля. Например:

REPLACE="
/system/app/YouTube
/system/app/Bloatware
"

В этом списке будут автоматически созданы каталоги `$MODPATH/system/app/YouTube` и `$MODPATH/system/app/Bloatware`, а затем выполнены команды `setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/YouTube` и `setfattr -n trusted.overlay.opaque -v y $MODPATH/system/app/Bloatware`. После вступления модуля в силу каталоги `/system/app/YouTube` и `/system/app/Bloatware` будут заменены на пустые.

::: tip различия с Magisk

В KernelSU бессистемный механизм реализован через overlayfs ядра, а в Magisk в настоящее время используется магическое монтирование (bind mount). Эти два метода реализации имеют существенные различия, но конечная цель у них одна: модификация файлов /system без физического изменения раздела /system.
:::

Если вы заинтересованы в использовании overlayfs, рекомендуется прочитать [документацию по overlayfs](https://docs.kernel.org/filesystems/overlayfs.html) ядра Linux.

### system.prop

Этот файл имеет тот же формат, что и `build.prop`. Каждая строка состоит из `[key]=[value]`.

### sepolicy.rule

Если для вашего модуля требуются дополнительные патчи sepolicy, добавьте эти правила в данный файл. Каждая строка в этом файле будет рассматриваться как утверждение политики.

## Установщик модулей {#module-installer}

Инсталлятор модуля KernelSU - это модуль KernelSU, упакованный в zip-файл, который может быть прошит в APP-менеджере KernelSU. Простейший установщик модуля KernelSU - это просто модуль KernelSU, упакованный в zip-файл.

```txt
module.zip
│
├── customize.sh                       <--- (Необязательно, более подробно позже)
│                                           Этот скрипт будет использоваться в update-binary
├── ...
├── ...  /* Остальные файлы модуля */
│
```

:::warning
Модуль KernelSU НЕ поддерживается для установки в пользовательское Recovery!!!
:::

### Персонализация {#customizing-installation}

Если вам необходимо настроить процесс установки модуля, то в качестве опции вы можете создать в программе установки скрипт с именем `customize.sh`. Этот скрипт будет _источником_ (не исполняться!) сценария установщика модуля после извлечения всех файлов и применения стандартных разрешений и secontext. Это очень удобно, если ваш модуль требует дополнительной настройки в зависимости от ABI устройства, или вам необходимо установить специальные разрешения/секонтекст для некоторых файлов модуля.

Если вы хотите полностью контролировать и настраивать процесс установки, объявите `SKIPUNZIP=1` в файле `customize.sh`, чтобы пропустить все шаги установки по умолчанию. При этом ваш `customize.sh` будет сам отвечать за установку.

Сценарий `customize.sh` запускается в оболочке BusyBox `ash` KernelSU с включенным "Автономным режимом". Доступны следующие переменные и функции:

#### Переменные

- `KSU` (bool): переменная, отмечающая, что скрипт выполняется в окружении KernelSU, причем значение этой переменной всегда будет true. Ее можно использовать для различения KernelSU и Magisk.
- `KSU_VER` (string): строка версии текущего установленного KernelSU (например, `v0.4.0`)
- `KSU_VER_CODE` (int): код версии текущего установленного KernelSU в пользовательском пространстве (например, `10672`)
- `KSU_KERNEL_VER_CODE` (int): код версии текущей установленной KernelSU в пространстве ядра (например, `10672`)
- `BOOTMODE` (bool): в KernelSU всегда должно быть `true`.
- `MODPATH` (path): путь, по которому должны быть установлены файлы ваших модулей
- `TMPDIR` (path): место, где вы можете временно хранить файлы
- `ZIPFILE` (path): установочный zip-архив вашего модуля
- `ARCH` (string): архитектура процессора устройства. Значение: `arm`, `arm64`, `x86` или `x64`.
- `IS64BIT` (bool): `true`, если `$ARCH` имеет значение `arm64` или `x64`.
- `API` (int): уровень API (версия Android) устройства (например, `23` для Android 6.0)

::: warning
В KernelSU MAGISK_VER_CODE всегда равен 25200, а MAGISK_VER всегда равен v25.2. Пожалуйста, не используйте эти две переменные для определения того, запущен ли он на KernelSU или нет.
:::

#### Функции {#functions}

```txt
ui_print <msg>
    вывести <msg> на консоль
    Избегайте использования 'echo', так как он не будет отображаться в консоли пользовательского recovery

abort <msg>
    вывести сообщение об ошибке <msg> на консоль и завершить установку
    Избегайте использования команды 'exit', так как в этом случае будут пропущены шаги очистки завершения установки

set_perm <target> <owner> <group> <permission> [context]
    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
    Эта функция является сокращением для следующих команд:
       chown owner.group target
       chmod permission target
       chcon context target

set_perm_recursive <directory> <owner> <group> <dirpermission> <filepermission> [context]
    если [context] не задан, то по умолчанию используется "u:object_r:system_file:s0".
    для всех файлов в <directory> будет вызвана команда:
       set_perm file owner group filepermission context
    для всех каталогов в <directory> (включая себя самого), он вызовет:
       set_perm dir owner group dirpermission context
```

## Загрузочные сценарии {#boot-scripts}

В KernelSU скрипты делятся на два типа в зависимости от режима их работы: режим post-fs-data и режим late_start service:

- режим post-fs-data
  - Эта стадия является БЛОКИРУЮЩЕЙ. Процесс загрузки приостанавливается до завершения выполнения или по истечении 10 секунд.
  - Сценарии запускаются до того, как будут смонтированы какие-либо модули. Это позволяет разработчику модулей динамически настраивать свои модули до того, как они будут смонтированы.
  - Этот этап происходит до запуска Zygote, что практически означает, что все в Android
  - **ПРЕДУПРЕЖДЕНИЕ:** использование `setprop` приведет к блокировке процесса загрузки! Вместо этого используйте `resetprop -n <prop_name> <prop_value>`.
  - Запускайте скрипты в этом режиме только в случае необходимости.
- режим обслуживания late_start
  - Эта стадия является НЕБЛОКИРУЮЩЕЙ. Ваш скрипт выполняется параллельно с остальным процессом загрузки.
  - **Это рекомендуемый этап для запуска большинства скриптов.**

В KernelSU скрипты запуска делятся на два типа по месту их хранения: общие скрипты и скрипты модулей:

- Общие скрипты
  - Размещаются в файлах `/data/adb/post-fs-data.d` или `/data/adb/service.d`.
  - Выполняется только в том случае, если скрипт установлен как исполняемый (`chmod +x script.sh`)
  - Скрипты в `post-fs-data.d` выполняются в режиме post-fs-data, а скрипты в `service.d` - в режиме late_start service.
  - Модули не должны **НЕ** добавлять общие скрипты при установке
- Скрипты модуля
  - Размещаются в отдельной папке модуля
  - Выполняются только в том случае, если модуль включен
  - `post-fs-data.sh` запускается в режиме post-fs-data, а `service.sh` - в режиме late_start service.

Все загрузочные скрипты будут выполняться в оболочке BusyBox `ash` от KernelSU с включенным "Автономным режимом".
