{"id":25440216,"url":"https://github.com/raynersec/libsu","last_synced_at":"2025-09-10T19:37:50.425Z","repository":{"id":133435929,"uuid":"267628223","full_name":"RaynerSec/libsu","owner":"RaynerSec","description":"Android library providing APIs to a Unix (root) shell","archived":false,"fork":false,"pushed_at":"2020-05-29T07:01:43.000Z","size":9274,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-17T11:35:21.979Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/RaynerSec.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-28T15:35:53.000Z","updated_at":"2020-05-29T07:01:50.000Z","dependencies_parsed_at":null,"dependency_job_id":"35b75e0c-cf8d-4477-bf56-dd713aaddd23","html_url":"https://github.com/RaynerSec/libsu","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RaynerSec%2Flibsu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RaynerSec%2Flibsu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RaynerSec%2Flibsu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RaynerSec%2Flibsu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RaynerSec","download_url":"https://codeload.github.com/RaynerSec/libsu/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254349526,"owners_count":22056369,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-17T11:29:56.314Z","updated_at":"2025-05-15T13:34:21.621Z","avatar_url":"https://github.com/RaynerSec.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# libsu\n\n[![](https://jitpack.io/v/topjohnwu/libsu.svg)](https://jitpack.io/#topjohnwu/libsu) [![](https://img.shields.io/badge/libsu-Javadoc-blue.svg)](https://topjohnwu.github.io/libsu/)\n\nAn Android library that provides APIs to a Unix (root) shell.\n\nSome poorly coded applications requests a new shell (call `su`, or worse `su -c \u003ccommands\u003e`) for every single command, which is very inefficient. This library makes sharing a single, globally shared shell session in Android applications super easy: developers won't have to bother about concurrency issues, and with a rich selection of both synchronous and asynchronous APIs, it is much easier to create a powerful root app.\n\nOptionally, `libsu` comes with a whole suite of I/O classes, re-creating `java.io` classes but enhanced with root access. Without even thinking about command-lines, you can use `File`, `RandomAccessFile`, `FileInputStream`, and `FileOutputStream` equivalents on all files that are only accessible with root permissions. The I/O stream classes are carefully optimized and have very promising performance.\n\nAlso optionally, this library bundles with prebuilt `busybox` binaries. App developers can easily setup and create an internal `busybox` environment without relying on potentially flawed (or even no) external `busybox`.\n\nOne complex Android application using `libsu` for all root related operations is [Magisk Manager](https://github.com/topjohnwu/Magisk/tree/master/app).\n\n## Changelog\n\n[Link to Changelog](./CHANGELOG.md)\n\n## Download\n```java\nandroid {\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n}\nrepositories {\n    maven { url 'https://jitpack.io' }\n}\ndependencies {\n    def libsuVersion = '2.5.1'\n    implementation \"com.github.topjohnwu.libsu:core:${libsuVersion}\"\n\n    /* Optional: For using com.topjohnwu.superuser.io classes */\n    implementation \"com.github.topjohnwu.libsu:io:${libsuVersion}\"\n\n    /* Optional: For including prebuilt busybox binaries */\n    implementation \"com.github.topjohnwu.libsu:busybox:${libsuVersion}\"\n}\n```\n\n## Quick Tutorial\n\n### Configurations\nSet configurations in your MainActivity or Application class:\n\n```java\nstatic {\n    /* Shell.Config methods shall be called before any shell is created\n     * This is the why in this example we call it in a static block\n     * The followings are some examples, check Javadoc for more details */\n    Shell.Config.setFlags(Shell.FLAG_REDIRECT_STDERR);\n    Shell.Config.verboseLogging(BuildConfig.DEBUG);\n    Shell.Config.setTimeout(10);\n}\n```\n\n### Shell Operations\n\n```java\n// Run commands and get output immediately\nList\u003cString\u003e output = Shell.su(\"find /dev/block -iname boot\").exec().getOut();\n\n// Aside from commands, you can also load scripts from InputStream\nShell.Result result = Shell.su(getResources().openRawResource(R.raw.script)).exec();\n\n// You can get more stuffs from the results\nint code = result.getCode();\nboolean ok = result.isSuccess();\noutput = result.getOut();\n\n// Run commands and output to a specific List\nList\u003cString\u003e logs = new ArrayList\u003c\u003e();\nShell.su(\"cat /cache/magisk.log\").to(logs).exec();\n\n// Run commands in the background and don't care results\nShell.su(\"setenforce 0\").submit();\n\n// Run commands in the background and get results via a callback\nShell.su(\"sleep 5\", \"echo hello\").submit(result -\u003e {\n    /* This callback will be called on the main (UI) thread\n     * after the operation is done (5 seconds after submit) */\n    result.getOut();  /* Should return a list with a single string \"hello\" */\n})\n\n// Create a reactive callback List, and update the UI on each line of output\nList\u003cString\u003e callbackList = new CallbackList\u003cString\u003e() {\n    @MainThread\n    @Override\n    public void onAddElement(String s) {\n        /* This callback will be called on the main (UI) thread each time\n         * the list adds a new element (in this case: shell outputs a new line)*/\n        uiUpdate(s);  /* Some method to update the UI */\n    }\n};\nShell.su(\n    \"for i in 1 2 3 4 5;do\",\n    \"  echo $i\"\n    \"  sleep 1\"\n    \"done\",\n    \"echo 'countdown done!'\").to(callbackList).submit(result -\u003e {\n        /* Some stuffs cannot be acquired from callback lists\n         * e.g. return codes */\n        uiUpdate(result.getCode());\n    });\n\n// Also get STDERR\nList\u003cString\u003e stdout = new ArrayList\u003c\u003e();\nList\u003cString\u003e stderr = new ArrayList\u003c\u003e();\nShell.su(\"echo hello\", \"echo hello \u003e\u00262\").to(stdout, stderr).exec();\n```\n\n### I/O\nAdd `com.github.topjohnwu.libsu:io` as dependency to access the I/O wrapper classes:\n\n```java\n/* Treat files that require root access just like ordinary files */\nFile logs = SuFile.open(\"/cache/magisk.log\");\nif (logs.exists()) {\n    try (InputStream in = new SuFileInputStream(logs);\n         OutputStream out = new SuFileOutputStream(\"/data/magisk.log.bak\")) {\n        /* All file data can be accessed by Java Streams */\n\n        // For example, use a helper method to copy the logs\n        ShellUtils.pump(in, out);\n    } catch (IOException e) {\n        e.printStackTrace();\n    }\n}\n```\n\n### BusyBox\nThe I/O classes relies on several commandline tools. *Most* of the tools are availible in modern Android via `toybox` (Android 6+), however for compatibility and reliable/reproducible behavior (some applets included in `toybox` is not fully featured), it will be a good idea to have BusyBox included to the environment:\n\n```java\n/* If you want to bundle prebuilt busybox binaries with your app,\n * add com.github.topjohnwu.libsu:busybox as a dependency, and\n * register BusyBoxInstaller as an initializer. */\nShell.Config.addInitializers(BusyBoxInstaller.class);\n\n/* If your app only targets Magisk users, and you are not willing to\n * increase your APK size for the busybox binaries, you can tell libsu\n * to use Magisk's internal busybox */\nShell.Config.setFlags(Shell.FLAG_USE_MAGISK_BUSYBOX);\n```\n\n### Advanced\nInitialize shells with custom `Shell.Initializer`, similar to what `.bashrc` will do:\n\n```java\nclass ExampleInitializer extends Shell.Initializer {\n    @Override\n    public boolean onInit(Context context, Shell shell) {\n        try (InputStream bashrc = context.getResources().openRawResource(R.raw.bashrc)) {\n            // Load a script from raw resources\n            shell.newJob()\n                .add(bashrc)                            /* Load a script from resources */\n                .add(\"export ENVIRON_VAR=SOME_VALUE\")   /* Run some commands */\n                .exec();\n        }\n        return true;\n    }\n}\n\n// Register the class as an initializer\nShell.Config.addInitializers(ExampleInitializer.class);\n```\n\n## Example\n\nThis repo also comes with an example app (`:example`), check the code and play/experiment with it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraynersec%2Flibsu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraynersec%2Flibsu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraynersec%2Flibsu/lists"}