diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..5111a39 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +**Version Number Format:** `major.minor.patch` +**Release Date Format:** `yyyy-mm-dd` + +**Types of Changes:** +- **Added** for new features. +- **Changed** for changes in existing functionality. +- **Deprecated** for soon-to-be removed features. +- **Removed** for now removed features. +- **Fixed** for any bug fixes. +- **Security** in case of vulnerabilities. +## + + +## [Unreleased] + +`-` + + +## [0.1.0] - 2020-12-14 + +`-` +## + + +[unreleased]: https://github.com/agnostic-apollo/sudo/compare/v0.1.0...HEAD +[0.1.0]: https://github.com/agnostic-apollo/sudo/releases/tag/v0.1.0 diff --git a/FAQs_And_FUQs.md b/FAQs_And_FUQs.md new file mode 100644 index 0000000..a48bc09 --- /dev/null +++ b/FAQs_And_FUQs.md @@ -0,0 +1,11 @@ +### Frequently Asked Questions(FAQs) + +`-` +## + + +### Frequently Unasked Questions(FUQs) + +`-` +## + diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d575247 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019, agnostic-apollo, auth={ type=`scrypt-kdf`, key=`xY%I5l>AxK+n@o.]cjUp8&s2.NyFBr=&]PoMxQ5B-W(T)e+d(lDT*I7u=jjFK-UA-IA-BA` } + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6037eef --- /dev/null +++ b/README.md @@ -0,0 +1,1640 @@ +# sudo + +`sudo` is a wrapper script to drop to any [supported shell](#Supported-Shells) or execute shell script files or their text passed as an argument with `superuser (root)` context in [Termux App]. Check the [Usage](#Usage) and [Command Types](#Command-Types) sections for more info on what type of commands can be run. `sudo` stands for *superuser do*. + +The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like [SuperSU] or [Magisk] for the `sudo` script to work. + +Make sure to read the [Worthy Of Note](#Worthy-Of-Note) section, **specially the [RC File Variables](#rc-file-variables) section. This is very important, specially if you were previously using [termux-sudo by st42]**. + +To use `sudo` with [Termux:Tasker] plugin and [RUN_COMMAND Intent], check [Termux:Tasker] `Setup Instructions` section for details on how to them up. The [Tasker App] or your plugin host app must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file since the `$PREFIX/bin/sudo` absolute path is outside the `~/.termux/tasker/` directory. For android `>= 10`, the [Termux App] should also be granted `Draw Over Apps` permission so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. Check [Templates](#Templates) section for template tasks that can be run used to run `sudo` from `Termux:Tasker` plugin and `RUN_COMMAND Intent`. + +If you want to run commands in `termux` user context, check [tudo]. +## + + + +### Contents +- [Dependencies](#Dependencies) +- [Downloads](#Downloads) +- [Install Instructions For Termux In Android](#Install-Instructions-For-Termux-In-Android) +- [Current Features](#Current-Features) +- [Planned Features](#Planned-Features) +- [Usage](#Usage) +- [Command Types](#Command-Types) +- [Supported Shells](#Supported-Shells) +- [Command Options](#Command-Options) +- [Shell Home](#Shell-Home) +- [Shell RC Files](#Shell-RC-Files) +- [Shell History Files](#Shell-History-Files) +- [Modifying Default Values](#Modifying-Default-Values) +- [Examples](#Examples) +- [Templates](#Templates) +- [Passing Arguments](#Passing-Arguments) +- [Issues](#Issues) +- [Worthy Of Note](#Worthy-Of-Note) +- [Tests](#Tests) +- [FAQs And FUQs](#FAQs-And-FUQs) +- [Changelog](#Changelog) +- [Contributions](#Contributions) +- [Credits](#Credits) +- [Donations](#Donations) +## + + + +### Dependencies + +- [Termux App] + +Using `sudo` directly from inside `termux` terminal session does not have any specific version requirements, other than `bash` version `>= 4.1`. +However, to use `sudo` with [Termux:Tasker] plugin and [RUN_COMMAND Intent] requires the following versions to be installed. Check [Passing Arguments](#Passing-Arguments) section and [Termux:Tasker] `Setup Instructions` section for details. + +- [Termux App] version `>= 0.100` +- [Tasker App] version `>= 5.9.4.beta` +- [Termux:Tasker] version `>= 0.5` +## + + + +### Downloads + +Latest version is `v0.1.0`. + +- [GitHub releases](https://github.com/agnostic-apollo/sudo/releases). +## + + + +### Install Instructions For Termux In Android + +The `sudo` file should be placed in termux `bin` directory `/data/data/com.termux/files/usr/bin`. +It should have `termux` `uid:gid` ownership and have executable `700` permission before it can be run directly without `bash`. + +1. Download the `sudo` file. + + - Download to termux bin directory directly from github using `curl` using a non-root termux shell. + Run `pkg install curl` to install `curl` first. + - Latest release: + + `curl -L 'https://github.com/agnostic-apollo/sudo/releases/latest/download/sudo' -o "/data/data/com.termux/files/usr/bin/sudo"` + + - Specific release: + + `curl -L 'https://github.com/agnostic-apollo/sudo/releases/download/v0.1.0/sudo' -o "/data/data/com.termux/files/usr/bin/sudo"` + + - Master Branch *may be unstable*: + + `curl -L 'https://github.com/agnostic-apollo/sudo/raw/master/sudo' -o "/data/data/com.termux/files/usr/bin/sudo"` + + - Download `sudo` file manually from github to the android download directory and then copy it to termux bin directory. + + You can download the `sudo` file from a github release from the `Assets` dropdown menu. + + You can also download it from a specific github branch/tag by opening the `sudo` file from the `Code` section. + Right-click or hold the `Raw` button at the top and select `Download/Save link`. + + Then copy the file to termux bin directory using `cat` command below or use a root file browser to manually place it. + + `cat "/storage/emulated/0/Download/sudo" > "/data/data/com.termux/files/usr/bin/sudo"` + +2. Set `termux` ownership and executable permissions. + + - If you used a `curl` or `cat` to copy the file, then use a non-root termux shell to set ownership and permissions with `chown` and `chmod` commands respectively: + + `export termux_bin_path="/data/data/com.termux/files/usr/bin"; export owner="$(stat -c "%u" "$termux_bin_path")"; chown "$owner:$owner" "$termux_bin_path/sudo" && chmod 700 "$termux_bin_path/sudo";` + + - If you used a root file browser to copy the file, then use `su` to start a root shell to set ownership and permissions with `chown` and `chmod` commands respectively: + + `export termux_bin_path="/data/data/com.termux/files/usr/bin"; export owner="$(stat -c "%u" "$termux_bin_path")"; su -c "chown \"$owner:$owner\" \"$termux_bin_path/sudo\" && chmod 700 \"$termux_bin_path/sudo\"";` + + - Or manually set them with your root file browser. You can find `termux` `uid` and `gid` by running the command `id -u` in a non-root termux shell or by checking the properties of the termux `bin` directory from your root file browser. +## + + + +### Current Features + +- Allows dropping to an interactive shell in `termux` user context for any of the supported [Interactive Shells](#Interactive-Shells) with priority to either termux or android binary and library paths. +- Allows running single commands in `superuser (root)` context without having to start an interactive shell. +- Allows passing of script file paths or script text as arguments for any of the supported [Script Shells](#Script-Shells) to have them executed in `termux` user context without having to create physical script files first for the later case, like in `~/.termux/tasker/` directory for [Termux:Tasker]. +- Automatic setup of home directories, `rc` files, `history` files and working directories with proper ownership and permissions. +- Automatic setup of the shell environment and exporting of all required variables including `LD_PRELOAD` so that termux commands work properly, specifically if being run from [Termux:Tasker] or [RUN_COMMAND Intent]. +- Provides a lot of [Command Options](Command-Options) that are specifically designed for usage with [Termux:Tasker] and the [RUN_COMMAND Intent]. +## + + + +### Planned Features + +`-` +## + + + +### Usage + +``` +sudo is a wrapper script to drop to the supported shells or execute +shell script files or their text passed as an argument with super +user (root) context in termux. + + +Usage: + sudo [command_options] su + sudo [command_options] asu + sudo [command_options] [-p] [command_args] + sudo [command_options] -s [core_script_args] + + +Available command_options: + [ -h | --help ] display this help screen + [ --help-extra ] display more help about how sudo command works + [ --version ] display version + [ -v | -vv ] set verbose level to 1 or 2 + [ -a ] force set priority to android paths for path + command type + [ -b ] go back to last activity after running core_script + [ -B ] run core_script in background + [ -c ] clear shell after running core_script + [ -d ] disable stdin for core_script + [ -D ] disable preserve environment for su + [ -e ] exit early if core_script fails + [ -E ] exec interactive shell or the path command + [ -f ] force use temp script file for core_script + [ -F ] consider core_script to be a path to script file + instead of script text + [ -H ] same sudo post shell home as sudo shell home + [ -i ] run interactive sudo post shell after running + core_script + [ -l ] go to launcher activtiy after running core_script + [ -L ] export all existing paths in '$LD_LIBRARY_PATH' + variable + [ -n ] redirect stderr to /dev/null for core_script + [ -N ] redirect stdout and stderr to /dev/null for + core_script + [ -o ] redirect stderr to stdout for core_script + [ -O ] redirect stdout to stderr for core_script + [ -p ] set 'path' as command type [default] + [ -P ] export all existing paths in '$PATH' variable + [ -r ] parse commands as per RUN_COMMAND intent rules + [ -R ] use root for searching and validating paths + [ -s ] set 'script' as command type + [ -S ] same sudo post shell as sudo shell + [ --comma-alternative= ] + comma alternative character to be used for + the `-r` option instead of the default + [ --dry-run ] + do not execute sudo commands + [ --export-paths= ] + additional paths to export in PATH variable, + separated with colons ':' + [ --export-ld-lib-paths= ] + additional paths to export in LD_LIBRARY_PATH + variable, separated with colons ':' + [ --force-remount-ro ] + force remount rootfs and system partitions back + to ro after sudo commands + [ --hold[=] ] + hold sudo from exiting until string is entered, + defaults to any character if string is not passed + [ --hold-if-fail ] + if '--hold' option is passed, then only hold if + exit code of sudo does not equal '0' + [ --list-interactive-shells ] + display list of supported interactive shells + [ --list-script-shells ] + display list of supported script shells + [ --no-create-rc ] + do not create rc files automatically + [ --no-create-hist ] + do not create history files automatically + [ --no-hist ] + do not save history for sudo shell and sudo post + shell + [ --no-log-args ] + do not log arguments and core_script content + when verbose mode is enabled + [ --no-remount-ro ] + do not remount rootfs and system partitions back + to ro after sudo commands + [ --keep-temp ] + do not delete sudo temp directory on exit + [ --post-shell= ] + name or absolute path for sudo post shell + [ --post-shell-home= ] + absolute path for sudo post shell home + [ --post-shell-options= ] + additional options to pass to sudo post shell + [ --post-shell-post-commands= ] + bash commands to run after sudo post shell + [ --post-shell-pre-commands= ] + bash commands to run before sudo post shell + [ --post-shell-stdin-string= ] + string to pass as stdin to sudo post shell + [ --remove-prev-temp ] + remove temp files and directories created on + previous runs of sudo command + [ --script-decode ] + consider the core_script as base64 + encoded that should be decoded before execution + [ --script-name= ] + filename to use for the core_script temp file + created in '.sudo.temp.XXXXXX' directory instead + of 'sudo_core_script' + [ --script-redirect= ] + core_script redirect mode for stdout and stderr + [ --shell= ] + name or absolute path for sudo shell + [ --shell-home= ] + absolute path for sudo shell home + [ --shell-options= ] + additional options to pass to sudo shell + [ --shell-post-commands= ] + bash commands to run after sudo shell for script + command type + [ --shell-pre-commands= ] + bash commands to run before sudo shell + [ --shell-stdin-string= ] + string to pass as stdin to sudo shell for script + command type + [ --sleep= ] + sleep for x seconds before exiting sudo + [ --sleep-if-fail ] + if '--sleep' option is passed, then only sleep if + exit code of sudo does not equal '0' + [ --su-env-options= ] + additional options to pass to su that sets up the + sudo environment + [ --su-run-options= ] + additional options to pass to su that runs the + final sudo command_type command + [ --title= ] + title for sudo shell terminal + [ --work-dir=<path> ] + absolute path for working directory + + +Set verbose level to 1 or 2 to get more info when running sudo command. + +Pass '--dry-run' option with verbose mode enabled to see the commands +that will be run without actually executing them. + +Visit https://github.com/agnostic-apollo/sudo for more help on how +sudo command works. + + + +The 'su' command type drops to an interactive shell in superuser (root) +context for any of the supported interactive shells. To drop to a root +'bash' shell, just run 'sudo su'. The priority will be set to termux +bin and library paths in '$PATH' and '$LD_LIBRARY_PATH' variables. +Use the '--shell' option to set the interactive shell to use. + + +The 'asu' command type is the same as 'su' command type but +instead the priority will be set to android bin and library paths in +'$PATH' and '$LD_LIBRARY_PATH' variables. +Use the '--shell' option to set the interactive shell to use. + + +The 'path' command type runs a single command in superuser (root) +context. You can use it just by running 'sudo <command> [command_args]' +where 'command' is the executable you want to run and 'command_args' +are any optional arguments to it. The 'command' will be run within a +'bash' shell. Priority is given to termux bin and library paths unless +'command' exists in '/system' partition. +To call the 'su' binary, run the 'sudo -p su [user]' command. + + +The 'script' command type takes any script text or path to a script +file for any of the supported script shells referred as 'sudo shell', +and executes the script with any optional arguments with the desired +script shell. This can be done by running the +'sudo -s <core_script> [core_script_args]' command. +The 'core_script' will be considered a 'bash' script by default. +The 'core_script' will be passed to the desired shell using +process substitution or after storing the 'core_script' in a temp file +in a temp directory in 'sudo shell' home +'$HOME/.sudo.temp.XXXXXX/sudo_core_script' and passing the path to +the desired shell, where 'XXXXXX' is a randomly generated string. +The method is automatically chosen based on the script shell +capabilities. The '-f' option can be used to force the usage of a +script file. The '-F' option can passed so that the 'core_script' +is considered as a path to script file that should be passed to +'sudo shell' directly instead of considering it as a script text. +Use the '--shell' option to set the script shell to use. +Use the '--post-shell' option to set the interactive shell to use if +'-i' option is passed. + + +Run "exit" command of your shell to exit interactive shells and return +to the termux shell. +``` +## + + + +### Command Types + + +#### `su` + +The `su` command type drops to an interactive shell in `superuser (root)` context for any of the supported [Interactive Shells](#Interactive-Shells). `su` stands for *substitute user* which in this case will be the `superuser (root)`. To drop to a root `bash` shell, just run `sudo su`. The priority will be set to termux bin and library paths in `$PATH` and `$LD_LIBRARY_PATH` variables. Check the [PATH and LD_LIBRARY_PATH Priorities](#path-and-ld_library_path-priorities) section for more info. + +Note that `su` is just a command type and does not represent the `su` binary itself. Use the `path` command type to run the `sudo -p su [user]` command instead for calling the `su` binary. +   + + + +#### `asu` + +The `asu` command type is the same as `su` command type but instead the priority will be set to android bin and library paths in `$PATH` and `$LD_LIBRARY_PATH` variables. Check the [PATH and LD_LIBRARY_PATH Priorities](path-and-ld_library_path-priorities) section for more info. +   + + + +#### `path` + +The `path` command type runs a single command in `superuser (root)` context. You can use it just by running `sudo <command> [command_args]` where `command` is the executable you want to run and `command_args` are any optional arguments you want to pass to it. + +The `command` will be run within a `bash` shell. Priority is given to termux bin and library paths unless `command` exists in `/system` partition. `sudo <command>` will not work if executable to be run does not have proper ownership or executable permissions set that disallows `termux` user to read or execute it if `sudo` command itself is being run from the `termux` context and `-R` option is not passed. The `command` must be an `absolute path` to an executable, or `basename` to an executable in the current directory or in a directory listed in the final `$PATH` variable that is to be exported by the `sudo` command. If it is not found, `sudo` will exit with an error. + +The `path` command type is of course useful for running single commands with root context without having to drop to a root shell, but its also very useful for running commands in `/system` partition that require priorities to be set to android library paths and which fail otherwise with errors like `CANNOT LINK EXECUTABLE` and `cannot locate symbol some_symbol referenced by /lib....`. The `sudo` command will automatically detect if the `command` exists in `/system` partition and set priorities to android bin and library paths in `$PATH` and `$LD_LIBRARY_PATH` variables. So running `sudo dumpsys` will just work. You can also force setting priority to android paths by passing the `-a` option or to run a binary in `/system` partition instead of that in termux bin paths. + +You can also use `sudo <command>` even if you are inside of a `sudo su` root shell and it will work without having to switch to `sudo asu` or exporting variables to change priority. + +You can also run the `sudo -p su [user]` or `sudo -p /path/to/su [user]` commands to call the `su` binary for dropping to a shell for a specific user or even run a command for a specific user, like `sudo -p su -c "logcat" system`. Note that if you do not provide an absolute path to the `su` binary and just run `sudo -p su`, then the termux `su` wrapper script will be called which is stored at `$PREFIX/bin/su` which automatically tries to find the `su` binary and unsets `LD_LIBRARY_PATH` and `LD_PRELOAD` variables. You can check its contents with `cat "$PREFIX/bin/su"`. The variables will be also be unset by the `sudo` script if it detects you are trying to run a `su` binary. + +Check the `-a` and `-r` command options that can be specifically used with the `path` command type. +   + + + +#### `script` + +The `script` command type takes any script text or path to a script file for any of the supported [Script Shells](#Script-Shells) referred as `sudo shell`, and executes the script with any optional arguments with the desired script shell. This can be done by running the `sudo -s <core_script> [core_script_args]` command. The `core_script` will be considered a `bash` script by default. + +The `script` command type is incredibly useful for usage with termux plugins like [Termux:Tasker] or [RUN_COMMAND Intent]. Currently, any script files that need to be run need to be created in `~/.termux/tasker/` directory, at least for `Termux:Tasker`. It may get inconvenient to create physical script files for each type of command you want to run. These script files are also neither part of backups of plugin host apps like Tasker and require separate backup methods and nor are part of project configs shared with other people or even between your own devices, and so the scripts need to be added manually to the `~/.termux/tasker/` directory on each device. To solve such issues and to dynamically define scripts of different interpreted languages inside your plugin host app like `Tasker` and to pass them to `Termux` as arguments instead of creating script files, the `script` command type can be used. The termux environment will also be properly loaded like setting `LD_PRELOAD` etc before running the commands. + +The `core_script` will be passed to the desired shell using [Process Substitution] or after storing the `core_script` in a temp file in a temp directory in `sudo shell` home `$HOME/.sudo.temp.XXXXXX/sudo_core_script` and passing the path to the desired shell, where `XXXXXX` is a randomly generated string. The method is automatically chosen based on the script shell capabilities. The `-f` option can be used to force the usage of a script file. If the temp directory is created, it will be empty other than the `sudo_core_script` file and will be unique for each execution of the script, which the script can use for other temporary stuff without having to worry about cleanup since the temp directory will be automatically removed when `sudo` command exits unless `--keep-temp` is passed. The temp directory path will also be exported in the `$SUDO_SCRIPT_DIR` environment variable which can be used by the `core_script`, `post shell` and `--*shell-*-commands` options, like `--shell-pre-commands='cd "$SUDO_SCRIPT_DIR"'`. The `$HOME` refers to the `sudo shell` home. + +For `bash zsh fish ksh python python2 ruby perl lua5.2 lua5.3 lua5.4`, process substitution is used by default and for `dash sh node php` a file is used. If the usage of process substitution is breaking for some complex scripts of some specific shell, please report the issue. + +The `-F` option can be passed so that the `core_script` is considered as a path to a script file that should be passed to `sudo shell` directly instead of considering it as a script text. + +The `core_script` can optionally not be passed or passed as an empty string so that other "features" of the `script` command type can still be used without calling the script shell. + +It may also be important to automatically open an interactive shell after the `core_script` completes. This can be done by using the `-i` option along with `--post-shell*` options. The `sudo post shell` can be any of the supported [Interactive Shells](#Interactive-Shells) and defaults to `bash`. The same shell as the script `sudo shell` can also be used for `sudo post shell` by passing the `-n` option as long as the `sudo shell` exists in the list of supported interactive shells. The environment variable `$SUDO_SCRIPT_EXIT_CODE` will be exported containing the exit code of the `core_script` before the interactive shell is started. Running an interactive shell will also keep the terminal session open after commands complete which is normally closed automatically when commands are run with the plugin or intents, although the `--hold` option can also be used for this. + +You can define your own exit traps inside the `core_script`, but **DO NOT** define them outside it with the `--*shell-*-commands` options since `sudo` defines its own trap function `sudo_script_trap` for cleanup, killing child processes and to exit with the trap signal exit code. If you want to handle traps outside the `core_script`, then define a function named `sudo_script_custom_trap` which will automatically be called by `sudo_script_trap`. The function will be sent `TERM`, `INT`, `HUP`, `QUIT` as `$1` for the respective trap signals. For the `EXIT` signal the `$1` will not be passed. Do not `exit` inside the `sudo_script_custom_trap` function. If the `sudo_script_custom_trap` function exits with exit code `0`, then the `sudo_script_trap` will continue to exit with the original trap signal exit code. If it exits with exit code `125` `ECANCELED`, then `sudo_script_trap` will consider that as a cancellation and will just return without running any other trap commands. If any other exit code is returned, then the `sudo_script_trap` will use that as exit code instead of the original trap signal exit code. + +Check the `-b`, `-B`, `-c`, `-d`, `-e`, `-E`, `-f`, `-F`, `-l`, `-n`, `N`, `-o`, `O`, `-r`, `--remove-prev-temp`, `--keep-temp`, `--shell*`, `--post-shell*`, `--script-decode`, `--script-redirect`, `--script-name` command options that can be specifically used with the `script` command type. +## + + + +### Supported Shells + +The `bash` shell is the default interactive and script shell and must exist at `$PREFIX/bin/bash` with ownership and permissions allowing `termux` user to read and execute it. The `--shell` and `--post-shell` options can be used to change the default shells. The `path` command type always uses the `bash` shell and command options are ignored. Normally, shells are not validated as the root user unless `-R` is passed so they must have proper ownership or executable permissions set that allows `termux` user to read and execute them. + +The exported environmental variables `$SUDO_SHELL_PS1` and `$SUDO_POST_SHELL_PS1` can be used to change the default `$PS1` values of the shell, provided that the shell uses it. Check the [Modifying Default Values](#Modifying-Default-Values) section for more info on `sudo` environmental variables and modifying default values. + + +#### Interactive Shells + +The supported interactive shells are: `bash zsh dash sh fish python ruby pry node perl lua5.2 lua5.3 lua5.4 php python2 ksh` + +These shells can be used for the `su` and `asu` command types like `sudo --shell=<shell> su` and `sudo --shell=<shell> asu` and also as post shell for `script` command type when the `-i` option is passed like `sudo -si --post-shell=<shell> <core_script>` to start an interactive shell after script commands complete. + +The `bash` shell is automatically chosen as the default interactive shell if the `--shell` or `--post-shell` options are not passed to set a specific shell. You can pass the name of a shell listed in the supported shells list like `--shell=zsh` or an absolute path like `--shell=/path/to/zsh`. The `$PREFIX/` and `~/` prefixes are also supported, like `$PREFIX/bin/zsh` or `~/zsh`. + + +For `perl`, the interactive shell is started using `rlwrap`, which must be installed. Use `pkg install rlwrap` to install. + + +#### Script Shells + +The supported script shells are: `bash zsh dash sh fish python ruby node perl lua5.2 lua5.3 lua5.4 php python2 ksh` + +These shells can be used for the `script` command type like `sudo -s --shell=<shell> <core_script>`. + +The `bash` shell is automatically chosen as the default script shell if the `--shell` option is not passed to set a specific shell. You can pass the name of a shell listed in the supported shells list like `--shell=zsh` or an absolute path like `--shell=/path/to/zsh`. The `$PREFIX/` and `~/` prefixes are also supported, like `$PREFIX/bin/zsh` or `~/zsh`. +## + + + +### Command Options + +The `$PREFIX/` and `~/` prefixes are supported for all command options that take in absolute paths as arguments. The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. Note that if the paths with shortcuts are not surrounded with single quotes, they will expanded by the local shell before being passed to the `sudo` script instead of the `sudo` script manually expanding them. Note that `~/` will expand to the shell or post shell home and not the necessarily the termux home if used inside scripts or the `*-commands` options. + +Its the users responsibility to properly quote all arguments passed to command options and also for any values like paths passed inside the arguments, specifically the `*-commands` and `*-options` options, so that whitespace splitting does not occur. + +Check [Arguments and Result Data Limits](#Arguments-and-Result-Data-Limits) for details on the max size of arguments that you can pass to `sudo` script, specifically the size of `core_script` and its arguments for the `script` command type. + + +- `-v | -vv` options can be used to increase the verbose level of the `sudo` command. Useful to see script progress and what commands will actually be run. You can also use verbose mode with the `--dry-run` option to see what commands will be run without actually executing them. + + +- `-a` option can be used with the `path` command type to force setting priority to android bin and library paths in `$PATH` and `$LD_LIBRARY_PATH` variables. This can be useful for cases when the `command` is an absolute path but does not exist in the `/system` partition but still needs priority to be set to android paths or if the `command` is a just the basename and you want to run the binary in `/system` partition instead of the one in termux bin path since that will be found first during the search since the `$PATH` variable will be set to priority to android paths. + + +- `-b` option can be used with the `script` command type mainly for when commands are to be run in a foreground terminal session from plugins. This will simulate double back button press once the `core_script` is complete to go to the last activity, first to close keyboard and second to close terminal session. Use this only for short scripts, otherwise the user may have switched from the terminal session to a different app and back buttons simulation would be done inside that app instead. + + +- `-B` option can be used with the `script` command type to run the `core_script` in background with `&` (not the entire `sudo` command). This can be used with the `-i` option or even with the `--shell-post-commands` option. The `pid` of the background process will be available in the `$SUDO_SCRIPT_PID` variable. Note that all child processes are killed when `sudo` exits. + + +- `-c` option can be used with the `script` command type mainly for when commands are to be run in a foreground terminal session from plugins and an interactive shell session needs to be opened after the `core_script` is complete with the `-i` option. This will clear the terminal session once the `core_script` is complete. + + +- `-d` option can be used with the `script` command type to disable `stdin` for the `core_script`. This will redirect the `stdin` to `/dev/null` and unset the `$PS1` variable so that the `core_script` can detect that the `stdin` is not available and run the script in a non-interactive mode. If the `core_script` doesn't check if `stdin` is available or not and still attempts to read, it will receive nothing as input or may even cause exceptions in some script shells if `I/O` exceptions are not handled properly. Note that when plugin commands are run in a foreground terminal session, then even though keyboard is not shown, `stdin` is available and can be requested by the script which will then open the keyboard. + + +- `-D` option can be used to disable preserve environment when running `su`, otherwise environment is always preserved. + + +- `-e` option can be used with the `script` command type to exit early if `core_script` fails due to an exit code other than `0` without running any commands meant to be run after the `core_script` like defined by `-b`, `-c`, `-i`, `-l`, `--post-shell-pre-commands` and `--post-shell-options` command options. If `-B` is passed, then this is ignored. + + +- `-E` option can be used to `exec` the `su` that runs the final `sudo` `command_type` command. The commands for `--hold` and `--sleep` options and remount to `ro` commands and any other commands that need to be run after the `sudo` `command_type` command will not be run. + + +- `-f` option can be used with the `script` command to force usage of `$HOME/.sudo.temp.XXXXXX/sudo_core_script` temp file for storing `core_script` for debugging or if for reason the shell variant doesn't support process substitution and the `sudo` command is automatically trying to use it and is failing. It can also be used to provide a unique temp directory that can be used by the `core_script` which will automatically be deleted after execution. + + +- `-F` option can be used with the `script` command to consider `core_script` as a path to script file that should be passed to `sudo shell` directly instead of considering it as a script text. + + +- `-H` option can be used with the `script` command type with the `-i` option to use the same interactive `sudo post shell` home as the script `sudo shell` home. This is useful for situations like if you are passing a custom path for `sudo shell` home and want to use the same for `sudo post shell` home instead of the default home used by the `sudo` script. So instead of running `sudo -si --shell-home=/path/to/home --post-shell-home=/path/to/home <core_script>`, you can simple run `sudo -siH --shell-home=/path/to/home <core_script>`. + + +- `-i` option can be used with the `script` command to open an interactive shell after the `core_script` completes, optionally specified by `--post-shell` option. If the `--post-shell` option is not passed, then the shell defaults to `bash`. + + +- `-l` option can be used with the `script` command type mainly for when commands are to be run in a foreground terminal session from plugins. This will simulate home button press once the `core_script` is complete to go to the launcher activity. + + +- `-L` option will export all the additional paths that already exist in the `$LD_LIBRARY_PATH` variable at the moment `sudo` command is run while running shells, The default paths exported by `sudo` command will still be exported and prefixed before the additional paths. You can also use the `--shell-pre-commands` and `--post-shell-pre-commands` options to manually export the `$LD_LIBRARY_PATH` variable with a different priority as long as it doesn't break execution of the shells. + + +- `-n` option can be used with the `script` command type to redirect `stderr` to `/dev/null` only for the `core_script` (not the entire `sudo` command). This is a shortcut for `--script-redirect=3`. + + +- `-N` option can be used with the `script` command type to redirect both `stdout` and `stderr` to `/dev/null` only for the `core_script` (not the entire `sudo` command). This is a shortcut for `--script-redirect=4`. + + +- `-o` option can be used with the `script` command type to redirect `stderr` to `stdout` only for the `core_script` (not the entire `sudo` command). This is a shortcut for `--script-redirect=0`. + + +- `-O` option can be used with the `script` command type to redirect `stdout` to `stderr` only for the `core_script` (not the entire `sudo` command). This is a shortcut for `--script-redirect=1`. + + +- `-p` option sets `path` as the command type for `sudo` and is the default command type. + + +- `-P` option will export all the additional paths that already exist in the `$PATH` variable at the moment `sudo` command is run while running shells, The default paths exported by `sudo` command will still be exported and prefixed before the additional paths. You can also use the `--shell-pre-commands` and `--post-shell-pre-commands` options to manually export the `$PATH` variable with a different priority as long as it doesn't break execution of the shells. + + +- `-r` option will parse arguments as per `RUN_COMMAND` intent rules. This will by default replace any comma alternate characters `‚` (`#U+201A`, `‚`, `‚`, `single low-9 quotation mark`) with simple commas `,` (`U+002C`, `,`, `,`, `comma`) found in any `command_args` for the `path` command type and in `core_script` and any `core_script_args` for the `script` command type. They will also be replaced in the `--hold`, `--post-shell-home`, `--post-shell-pre-commands`, `--post-shell-options`, `--shell-home`, `--shell-pre-commands`, `--shell-post-commands`, `--shell-options`, `--script-name`, `--su-env-options`, `--su-run-options`, `--title` and `--work-dir` command options passed **after** the `-r` option, so ideally `-r` option should be passed before any of them. You can use a different character that should be replaced using the `--comma-alternative` option. Check [Passing Arguments Using RUN_COMMAND Intent](#Passing-Arguments-Using-RUN_COMMAND-Intent) section for why this is may be required. + + +- `-R` option can be use to enable usage of `root` for searching and validating paths. This can be useful for cases where the `termux` user does not have the read or execute permissions to shell or other paths. Starting new `su` shells for validating paths increases execution time and hence is not done by default. + + +- `-s` option sets `script` as the command type for `sudo`. + + +- `-S` option can be used with the `script` command type with the `-i` option to use the same interactive `sudo post shell` as the script `sudo shell` as long as the `sudo shell` exists in the list of supported interactive shells. This is useful for situations like if you are running a `python` script and want to start a `python` interactive shell after the script completes instead of the likely default `bash` shell. So instead of running `sudo -si --shell=python --post-shell=python <core_script>`, you can simple run `sudo -siS --shell=python <core_script>`. + + +- `--comma-alternative` option can be used to set the comma alternative character to be used for the `-r` option instead of the default. + + +- `--dry-run` option will enable dry running of the `sudo` script. This will not execute any commands, nor will `rc` files, `history` files or `working directory` passed be created. However, the `sudo shell` home and `$HOME/.sudo.temp.XXXXXX/sudo_core_script` file will still be created if `sudo_core_script` file needs to be created. It's advisable to also pass the `-v` or `-vv` options along with this to see script progress and what commands would actually have been run. Passing `--keep-temp` may also be useful. + + +- `--export-paths` option can be used to set the additional paths to export in `$PATH` variable, separated with colons `:`. The string passed must not start or end with or contain two consecutive colons `:`. + + +- `--export-ld-lib-paths` option can be used to set the additional paths to export in `$LD_LIBRARY_PATH` variable, separated with colons `:`. The string passed must not start or end with or contain two consecutive colons `:`. + + +- `--force-remount-ro` will enable force remount of rootfs `/` and system `/system` partitions back to `ro` mode in `Global` namespace after `sudo` commands complete regardless of if they were mounted as `rw` or `ro` when `sudo` command was run and were mounted as `rw` due to home or working directories in those partitions. + + +- `--hold[=<string>]` option can be used to make `sudo` script hold the terminal and not exit until the `string` is entered. The `string` can only contain alphanumeric and punctuation characters without newlines specified by `[:alnum:]` and `[:punct:]` bash regex character classes. If only `--hold` is passed, then `sudo` will exit after any key is pressed. This is useful for cases where `sudo` is being run in a foreground terminal session, like from plugins and the terminal closes as soon as the `sudo` exits, regardless of if `sudo` failed or was successful without the user getting a chance to see the output. + + +- `--hold-if-fail` option can be used with the `--hold` option to only hold if exit code of `sudo` does not equal `0`. + + +- `--list-interactive-shells` option can be used to display the list of supported [Interactive Shells](#Interactive-Shells) and exit. + + +- `--list-script-shells` option can be used to display the list of supported [Script Shells](#Script-Shells) and exit. + + +- `--no-create-rc` option will disable automatic creation of `rc` files for `sudo shell` and `sudo post shell` if they are missing. + + +- `--no-create-hist` option will disable automatic creation of `history` files for `sudo shell` and `sudo post shell` if they are missing. + + +- `--no-hist` option will try to disable history loading and saving for `sudo shell` and `sudo post shell` depending on shell capabilities. Not all interactive shells have history support or of disabling it. The history files will also not be created automatically if they are missing. + + +- `--no-log-args` option can be used with the `path` or `script` command type to disable logging of arguments and `core_script` content when verbose mode is enabled. This is useful in cases where the arguments or `core_script` content is too large and it "hides" the other useful log entries due to terminal session output buffer limitations. + + +- `--no-remount-ro` will disable remount of rootfs `/` and system `/system` partitions back to `ro` mode in `Global` namespace after `sudo` commands complete if they were mounted as `rw` when `sudo` command was run due to home or working directories in those partitions. + + +- `--keep-temp` option will disable automatic deletion of the sudo temp directory `$HOME/.sudo.temp.XXXXXX` on exit. This can be used to debug any temp script files created. + + +- `--post-shell=<shell>` option can be used with the `script` command type to pass the name or absolute path for `sudo post shell` to be used with the `script` command type and the `-i` option. + + +- `--post-shell-home=<path>` option can be used with the `script` command type to pass an absolute path for the `sudo post shell` home that overrides the default value. + + +- `--post-shell-options=<options>` option can be used with the `script` command type to set additional options to pass to `sudo post shell` while starting an interactive shell. + + +- `--post-shell-post-commands=<commands>` option can be used with the `script` command type to set bash commands to be run after the `sudo post shell` exits. + + +- `--post-shell-pre-commands=<commands>` option can be used with the `script` command type to set bash commands to be run before the `sudo post shell` is started. The commands are run after the commands that are run for the `--shell-post-commands`, `-b`, `-l` and `-c` options. + + +- `--post-shell-stdin-string=<string>` ] option can be used with the `script` command type to set the string that should be passed as `stdin` to the `sudo post shell` using process substitution or herestring depending on shell capabilities. Some shells when run in interactive mode may automatically exit after running the commands received through `stdin` or may not even accept strings from `stdin`. This option is used for automated testing. + + +- `--remove-prev-temp` option can be used with the `script` command type to remove temp files and directories created on previous runs of `sudo` command that may have been left behind due to `sudo` being killed and not cleanly exiting, or Termux crashing or being killed by android `OOM` killer or the phone rebooting as long as the `sudo shell` home is not changed. + + +- `--script-decode` option can be used with the `script` command type so that the `core_script` passed is considered as a `base64` encoded string that should be decoded and stored in temp file. The temp file path is passed to the script shell. This can be useful to pass a script whose normal decoded form contains non `UTF-8` or binary data which if passed directly as an argument may be discarded by the shell if not encoded first since such data cannot be stored in bash variables. If this is passed, then `-r` option processing will be ignored for the `core_script` but not for any arguments. + + +- `--script-name` option can be used with the `script` command type to set the filename to use for the `core_script` temp file created in `$HOME/.sudo.temp.XXXXXX/sudo_core_script` directory instead of `sudo_core_script`. The temp file path is passed to the script shell if `-f` or `--script-decode` is passed or if the script shell doesn't support process substitution or if `core_script` passed contained non `UTF-8` or binary data. + + +- `--script-redirect=<mode/string>` option can be used with the `script` command type to set the redirect mode or string for `stdout` and `stderr` for the `core_script`. The following modes are supported: + + - `0` redirect `stderr` to `stdout`. This can be used to receive both `stdout` and `stderr` in a synchronized way as `stdout`, like in `%stdout` variable for `Termux:Tasker` plugin for easier processing of result of commands. + + - `1` redirect `stdout` to `stderr`. This can be used to receive both `stdout` and `stderr` in a synchronized way as `stderr`, like in `%stderr` variable for `Termux:Tasker` plugin for easier processing of result of commands. + + - `2` redirect `stdout` to `/dev/null`. This can be used to ignore `stdout` output of the `core_script`. + + - `3` redirect `stderr` to `/dev/null`. This can be used to ignore `stderr` output of the `core_script`. + + - `4` redirect `stdout` and `stderr` to `/dev/null`. This can be used to ignore `stdout` and `stderr` output of the `core_script`. + + - `5` redirect `stderr` to `stdout` and `stdout` to `stderr`. This can be used to swap `stdout` and `stderr` output of the `core_script`. + + - `6` redirect `stderr` to `stdout` and `stdout` to `/dev/null`. This can be used to ignore `stdout` and to receive `stderr` output as `stdout` of the `core_script`. + + - `*` else it is considered a string that's appended after the `core_script` and its arguments. This can be used for custom redirection, like redirection to a file and possibly used along with the `--shell-pre-commands` option if some prep is required. + + Note that anything sent to `stdout` and `stderr` outside the `core_script` shell will still be sent to `stdout` and `stderr` and will be received in the `%stdout` and `%stderr` variables for `Termux:Tasker` plugin, so do not ignore them completely while checking for failures. + + If you are using `SuperSU` and running commands in an interactive shell like from a foreground terminal session, then these options will not work properly. Check [Automatic redirection of stderr to stdout in SuperSU](#automatic-redirection-of-stderr-to-stdout-in-supersu) for more details. + + +- `--shell=<shell>` option can be used to pass the name or absolute path for `sudo shell`. For `su` and `asu` command types, this is refers to the interactive shell. For `script` command type, this refers to script shell that should run the `core_script`. For `path` command type, this option is ignored. + + +- `--shell-home=<path>` option can be used to pass an absolute path for the `sudo shell` home that overrides the default value. + + +- `--shell-options=<options>` option can be used to set additional options to pass to `sudo shell`. For `su` and `asu` command types, these will be passed while starting an interactive shell. For `script` command type, these will be passed while starting the script shell that will be used to passed the `core_script`. For `path` command type, these options are ignored. + + +- `--shell-post-commands=<commands>` option can be used with the `script` command type to set bash commands to be run after the `sudo shell` running the `core_script` exits. The commands are run before the commands that are run for the `-b`, `-l`, `-c` and `--post-shell-pre-commands` options. + + +- `--shell-pre-commands=<commands>` option can be used to set bash commands to be run before the `sudo shell` is started. For `su`, `asu` and `path` command types, these commands must be simple commands, (preferably one liners) where each command **must** end with a semicolon `;` since they are passed using the `-c` option to `su` that is running a `bash` shell using its `--shell` option. For the `script` command type, these can be more complicated, like a bash script itself, since they are passed to a new `bash` shell in a pseudo file. The commands are run after the `cd` command for `--work-dir` is run. + + +- `--shell-stdin-string=<string>` ] option can be used with the `su` `asu` and `script` command type to set the string that should be passed as `stdin` to the `sudo shell` using process substitution or herestring depending on shell capabilities. Some shells when run in interactive mode may automatically exit after running the commands received through `stdin` or may not even accept strings from `stdin`. This option is used for automated testing. + + +- `--sleep=<seconds>` option can be used to make `sudo` script to sleep for `x` seconds before exiting. Seconds can be an integer or floating point number that is passed to the `sleep` command. This is useful for cases where `sudo` is being run in a foreground terminal session, like from plugins and the terminal closes as soon as the `sudo` exits, regardless of if `sudo` failed or was successful without the user getting a chance to see the output. + + +- `--sleep-if-fail` option can be used with the `--sleep` option to only sleep if exit code of `sudo` does not equal `0`. + + +- `--su-env-options=<options>` option can be used to set additional options to pass to `su` that set up the `sudo` environment. The `-c` option and `user` argument is not supported. Use `sudo -p su [user]` command instead. + + +- `--su-run-options=<options>` option can be used to set additional options to pass to `su` that runs the final `sudo` `command_type` command. The `-c` option and `user` argument is not supported. Use `sudo -p su [user]` command instead. + + +- `--title=<title>` option can be used to set the title for the foreground terminal session, that is shown in the `termux` sidebar. + + +- `--work-dir=<path>` option can be used to set the absolute path for working directory for the `sudo shell`. The `cd` command is run before the commands passed with `--shell-pre-commands` and `--post-shell-pre-commands` options are run. The directory will be automatically created if missing. +## + + + +### Shell Home + +The default `$HOME` directory for `sudo shell` and `sudo post shell` is `/data/data/com.termux/files/home/.suroot`. The `--shell-home` and `--post-shell-home` options or the exported environmental variables `$SUDO_SHELL_HOME` and `$SUDO_POST_SHELL_HOME` can be used to change the default directory. The home directory should ideally be different from the termux home directory to keep `config`, `rc` and `history` files separate for the `root` user and the `termux` user. The home directory should also be owned by the `root` user and have `0700` permission so that `non-root` users cannot access it for security reasons and hence termux home should ideally not be used. + +Check the [Modifying Default Values](#Modifying-Default-Values) section for more info on `sudo` environmental variables and modifying default values. + +If the home directory is under the termux files directory, then it must not be one of the following directories: `~/.{cache,config,local,termux}` and `$PREFIX/*`. + +The home directory is automatically created when `sudo` command is run if it does not exist. The `root:root` ownership and `700` permission is also set to it. + +If the home or working directories are in android rootfs `/` partition or android system `/system` partition, then the respective partition is automatically remounted as `rw` in the `Global` namespace when `sudo` command is run and remounted back to `ro` before `sudo` command exits, but only if the partition was mounted as `ro` before `sudo` command was run or `--no-remount-ro` was not passed. The `--force-remount-ro` option can be passed to force remounting to `ro` regardless of partition mount state before `sudo` command was run. For android `>= 10`, do not set home or working directory in rootfs or system partition since `sudo` script will exit with error. In android `>= 10`, rootfs partition is likely a read-only system-as-root `SAR` partition and system partition is likely an `ext4` `dedup` filesystem which cannot be remounted as `rw`. + +If the `-E` option is passed or an `exec` is manually done, then remounting back to `ro` will not happen. +## + + + +### Shell RC Files + +The following shell `rc` files are used for different shells depending on if `sudo shell` or `sudo post shell` home is different from termux home or shared. The `rc` files are usually unique for different shells. + +- If the homes are different, then `sudo` shells and `termux` shells will have different `rc` files, stored in their own homes. + +- If the homes are shared and the shell has no `--rc` param or environmental variable for `rc` files, then `sudo` shells and `termux` shells will have to share the same `RCFILE`, implied by `(shared)` and `(hard-coded)` (by the shell) columns, otherwise will be different. + +For shells that do not have `rc` files have their columns set to `-`. + + +| Shell | RCFILE (different home) | RCFILE (shared home) | Set Method | +| ------- |:-----------------------:|:-----------------------:|:----------------:| +| bash | .bashrc | .sudo_bashrc | --rcfile | +| zsh | .zshrc | *(shared)* | $ZDOTDIR | +| dash | .dashrc | .sudo_dashrc | $ENV | +| sh | .shrc | .sudo_shrc | $ENV | +| fish | .config/fish/config.fish| *(shared)* | $XDG_CONFIG_HOME | +| python | .pythonrc | .sudo_pythonrc | $PYTHONSTARTUP | +| ruby | .irbrc | *(shared)* | *(hard-coded)* | +| pry | .pryrc | *(shared)* | *(hard-coded)* | +| node | - | - | - | +| perl | - | - | - | +| lua5.2 | - | - | - | +| lua5.3 | - | - | - | +| lua5.4 | - | - | - | +| php | php.ini | .sudo_php.ini | -c | +| python2 | .python2rc | .sudo_python2rc | $PYTHONSTARTUP | +| ksh | .kshrc | .sudo_kshrc | $ENV | + + +The `rc` file parent directory is automatically created when `sudo` command is run if it does not exist. The `root:root` ownership and `700` permission is also set to it. + +The `rc` file is automatically created when `sudo` command is run if it does not exist. The `root:root` ownership and `600` permission is also set to it. + +The `rc` file parent directory and `rc` file will not be created automatically if `-no-create-rc` is passed. +## + + + +### Shell History Files + + +The following shell `history` files are used for different shells depending on if `sudo shell` or `sudo post shell` home is different from termux home or shared. The `history` files are usually unique for different shells. + +- If the homes are different, then `sudo` shells and `termux` shells will have different `history` files, stored in their own homes. + +- If the homes are shared and the shell has no environmental variable for `history` files, then `sudo` shells and `termux` shells will have to share the same `HISTFILE`, implied by `(shared)` and `(hard-coded)` (by the shell) columns, otherwise will be different. + +For shells that do not have `history` files have their columns set to `-`. For shells whose history cannot be disabled have their `Disable Method` column set to `(not possible)`. + +For `pry` shell, the existing history will still be loaded, but new history will not be saved. + + +| Shell | HISTFILE (different home)| HISTFILE (shared home) | Set Method | Disable Method | +| ------- |:-----------------------------:|:----------------------------------:|:---------------------------:|:-----------------------------------:| +| bash | .bash_history | .sudo_bash_history | $HISTFILE | HISTFILE="/dev/null" | +| zsh | .zsh_history | .sudo_zsh_history | $HISTFILE | HISTFILE="/dev/null" | +| dash | .dash_history | .sudo_dash_history | $HISTFILE | HISTFILE="/dev/null" | +| sh | .sh_history | .sudo_sh_history | $HISTFILE | HISTFILE="/dev/null" | +| fish | .local/share/fish/fish_history| .local/share/fish/sudo_fish_history| $fish_history | --private | +| python | .python_history | *(shared)* | *(hard-coded)* | readline.write_history_file = *None | +| ruby | .irb_history | *(shared)* | *(hard-coded)* | *(not possible)* | +| pry | .pry_history | *(shared)* | *(hard-coded)* | Pry.config.history_save = false | +| node | .node_history | .sudo_node_history | $NODE_REPL_HISTORY | NODE_REPL_HISTORY="" | +| perl | .perl_history | .sudo_perl_history | `rlwrap` --history-filename | --history-filename "/dev/null" | +| lua5.2 | - | - | - | - | +| lua5.3 | - | - | - | - | +| lua5.4 | - | - | - | - | +| php | .php_history | *(shared)* | *(hard-coded)* | *(not possible)* | +| python2 | - | - | - | - | +| ksh | .ksh_history | .sudo_ksh_history | $HISTFILE | HISTFILE="/dev/null" | + + +The `history` file parent directory is automatically created when `sudo` command is run if it does not exist. The `root:root` ownership and `700` permission is also set to it. + +The `history` file is automatically created when `sudo` command is run if it does not exist. The `root:root` ownership and `600` permission is also set to it. + +The `history` file parent directory and `history` file will not be created automatically if `-no-create-hist` or `--no-hist` is passed. +## + + + +### Modifying Default Values + +Check the [sudo.config](sudo.config) file to see the environmental variables that can be used to change the default values. If the `sudo.config` file exits at `~/.config/sudo/sudo.config`, then `sudo` will automatically source it whenever it is run. It must have `termux` user ownership or be readable by it. + +You can download it from the `master` branch and set it up by running the following commands. If you are on an older version, you may want to extract it from its [release](https://github.com/agnostic-apollo/sudo/releases) instead. + +``` +config_directory="/data/data/com.termux/files/home/.config/sudo" +mkdir -p "$config_directory" && \ +chmod 700 -R "$config_directory" && \ +curl -L 'https://github.com/agnostic-apollo/sudo/raw/master/sudo.config' -o "$config_directory/sudo.config" && \ +chmod 600 "$config_directory/sudo.config" +``` + +You can use `shell` based text editors like `nano`, `vim` or `emacs` to modify the `sudo.config` file. + +`nano "/data/data/com.termux/files/home/.config/sudo/sudo.config"` + +You can also use `GUI` based text editor android apps that support `SAF`. Termux provides a [Storage Access Framework (SAF)](https://wiki.termux.com/wiki/Internal_and_external_storage) file provider to allow other apps to access its `~/` home directory. However, the `$PREFIX/` directory is not accessible to other apps. The [QuickEdit] or [QuickEdit Pro] app does support `SAF` and can handle large files without crashing, however, it is closed source and its pro version without ads is paid. You can also use [Acode editor] or [Turbo Editor] if you want an open source app. + +Note that the android default `SAF` `Document` file picker may not support hidden file or directories like `~/.config` which start with a dot `.`, so if you try to use it to open files for a text editor app, then that directory will not show. You can instead create a symlink for `~/.config` at `~/config_sym` so that it is shown. Use `ln -s "/data/data/com.termux/files/home/.config" "/data/data/com.termux/files/home/config_sym"` to create it. + + +If you use the `bash` shell in termux terminal session, you can optionally export the environmental variables like `$SUDO_SHELL_HOME` and `$SUDO_POST_SHELL_HOME` in the `~/.bashrc` file by adding `export SUDO_SHELL_HOME="/path/to/home"` and `export SUDO_POST_SHELL_HOME="/path/to/home"` lines to it so that they are automatically set whenever you start a terminal session. However, the `~/.bashrc` and `rc` files of other shells will not be sourced if you are running commands from `Termux:Tasker` or `RUN_COMMAND Intent`, and so it is advisable to use the `sudo.config` file instead, which will be sourced in all cases, regardless of how `sudo` is run. + +Note that `$SUDO_SHELL_PS1` and `$SUDO_POST_SHELL_PS1` values will not work if `$PS1` variable is overridden in `rc` files in `$PREFIX/etc/` or in `sudo shell` and `sudo post shell` homes. Check [RC File Variables](#rc-file-variables) section for more details. +## + + + +### Examples + +If you are using a foreground terminal session, then you must disable the `bash` command completion and history expansion for the current terminal session before running `sudo` commands to pass multi-line arguments by running `bind 'set disable-completion on'; set +H`. Otherwise `bash` will try to auto complete commands and search the history, and you will get prompts like `Display all x possibilities? (y or n)`. + + +#### `su` + +- Drop to an interactive `bash` shell in `superuser (root)` context with priority set to termux bin and library paths with the default configuration. + +`sudo su` + + +- Drop to an interactive `python` shell in `superuser (root)` context with priority set to termux bin and library paths. + +`sudo --shell=python --work-dir="~/" su` + + +- Drop to an interactive `bash` shell in `superuser (root)` context with priority set to termux bin and library paths with `/.suroot` directory as `sudo shell` home and remount to `ro` disabled before exiting `sudo`. Since the `/.suroot` directory is in rootfs `/` partition, it will automatically be mounted as `rw` when `sudo` command is run. + +`sudo --shell-home="/.suroot" --no-remount-ro su` + + +- Drop to an interactive `bash` shell in `superuser (root)` context with priority set to termux bin and library paths with `/.suroot` as the shell home and termux home as the working directory. All paths currently in `$PATH` and `$LD_LIBRARY_PATH` are also exported. + +`sudo -LP --shell-home="/.suroot" --work-dir='~/' su` + + +- Drop to an interactive `bash` shell in `superuser (root)` context with priority set to termux bin and library paths, do not store history, export some additional paths in `$PATH` variable, pass additional options to the bash interactive shell starting including a different rc/init file and run some commands before running the bash shell like exporting some variables and running a script. The value of the `--shell-options` option is surrounded with double quotes and the `--init-file` option value passed in it has double quotes escaped to prevent whitespace splitting when its passed to `bash`. The `--shell-pre-commands` option is instead surrounded with single quotes as an example and so doesn't need double quotes escaped but will require single quotes in commands to be escaped. Moreover, each command in the `--shell-pre-commands` option **must** end with a semicolon `;`. + +`sudo --no-hist --export-paths="/path/to/dir1:/path/to/dir2" --shell-options="--noprofile --init-file \"path/to/file\"" --shell-pre-commands='export VARIABLE_1="VARIABLE_VALUE_1"; export VARIABLE_2="VARIABLE_VALUE_2"; /path/to/script;' su` +## + + + +#### `asu` + +- Drop to an interactive `bash` shell in `superuser (root)` context with priority set to android bin and library paths with the default configuration. + +`sudo asu` +## + + + +#### `path` + +- Run `top` command to show top `10` processes of any user. + +`sudo top -m 10 -n 1` + + +- Run `ps` command to processes of all users. + +`sudo ps -ef` + + +- Run android `dumpsys` command. + +`sudo dumpsys` + + +- Run android `dumpsys` command and filter output to show only `termux` related entires. + +`sudo dumpsys | grep termux` +## + + + +#### `script` + + +##### `bash` + +- Pass a `bash` script text surrounded with single quotes that prints the first `2` args to `stdout`. There is normally no need to pass `--shell=bash` since `bash` shell would be the default shell. + +``` +sudo -s 'echo "Hi, $1 $2."' "bash" "shell" +``` + + + +- Pass a `bash` script text surrounded with single quotes that prints the first `2` args to `stdout` and start an interactive `bash` shell. + +``` +sudo -si 'echo "Hi, $1 $2."' "bash" "shell" +``` + + + +- Pass a `bash` script text surrounded with single quotes that prints the first `2` args to `stdout` and start an interactive `bash` shell. The script is forcefully stored in a temp file named `sudo_test` in a temp directory `$HOME/.sudo.temp.XXXXXX`, which is also not deleted after `sudo` exits. The title of the terminal session is also set to `sudo_test`. + +``` +sudo -sif --keep-temp --script-name="sudo_test" --title="sudo_test" 'echo "Hi, $1 $2."' "bash" "shell" +``` + + + +- Pass a path to `bash` script file to the `bash` shell instead of script text, with `2` args and start an interactive `bash` shell. + +``` +sudo -siF '~/.termux/tasker/termux_tasker_basic_bash_test' "bash" "shell" +``` + + + +- Pass a `bash` script text surrounded with single quotes that prints the first `2` args to `stdout` and run some commands before running the script like exporting some variables. The `--shell-pre-commands` option is surrounded with single quotes and so doesn't need double quotes escaped but will require single quotes in commands to be escaped. Moreover, complex commands can be passed as argument to the `--shell-pre-commands` option, which optionally may not end with a semicolon `;`. + +``` +sudo -s --shell-pre-commands=' +export VARIABLE_1="VARIABLE_VALUE_1" +export VARIABLE_2="VARIABLE_VALUE_2" +' ' +echo "Hi, $1 $2." +echo "VARIABLE_1=\`$VARIABLE_1\`" +echo "VARIABLE_2=\`$VARIABLE_2\`" +' "bash" "shell" +``` + + + +- Pass a `bash` script text surrounded with single quotes that prints the first `2` args to `stdout`and start an interactive `python` shell and run some commands before running the script, after running the script but before going to launcher activity, and after going to launcher activity but before starting the interactive `python` shell. + +``` +sudo -sil --post-shell="python" --shell-pre-commands='echo "Running script"' --shell-post-commands='echo "Script complete\nGoing to launcher"' --shell-pre-commands='echo "Starting interactive python shell"' 'echo "Hi, $1 $2."' "bash" "shell" +``` + + + +- Pass a `bash` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=bash <(cat <<'SUDO_EOF' +echo 'What is your name?' +read name +echo "Hi, $name." +SUDO_EOF +) +``` + + + +- Pass a `bash` script text surrounded with single quotes that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=bash ' +echo "What is your name?" +read name +echo "Hi, $name." +' +``` + + + +- Pass a `bash` script text surrounded with single quotes that reads a name from `stdin` and prints it to `stdout`, where the script itself also contains single quotes. + +``` +sudo -s --shell=bash ' +echo '\''What is your name?'\'' +read name +echo "Hi, $name." +' +``` + + + +- Pass a `bash` script text with process substitution that prints the first `2` args to `stdout` and start an interactive shell if only `2` args are received, otherwise exits early with an error without starting the interactive shell afterwards. Additional arguments that will be passed to the `core_script` are passed to `sudo` after the process substitution ends. + +``` +sudo -sie --shell=bash <(cat <<'SUDO_EOF' +#if parameter count is not 2 +if [ $# -ne 2 ]; then + echo "Invalid parameter count '$#' to 'termux_tasker_basic_bash_test'" 1>&2 + echo "$*" 1>&2 + exit 1 +fi + +echo "\$1=\`$1\`" +echo "\$2=\`$2\`" + +exit 0 +SUDO_EOF +) "hello," "termux!" +``` + + + +- Pass a `bash` script text surrounded with single quotes that redirects `stderr` of the `core_script` to `stdout` so that both `stdout` and `stderr` can be received in a synchronized way as `stdout`, like in `%stdout` variable for `Termux:Tasker` plugin for easier processing of result of commands. + +``` +sudo -so 'echo stdout; echo stderr 1>&2' +``` +   + + + + +##### `zsh` + +- Pass a `zsh` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=zsh <(cat <<'SUDO_EOF' +echo "Hi, $1 $2." +SUDO_EOF +) "zsh" "shell" +``` + + + +- Pass a `zsh` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=zsh <(cat <<'SUDO_EOF' +echo "What is your name?" +read name +echo "Hi, $name." +SUDO_EOF +) +``` +   + + + + +##### `fish` + +- Pass a `fish` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=fish <(cat <<'SUDO_EOF' +echo "Hi, $argv[1] $argv[2]." +SUDO_EOF +) "fish" "shell" +``` + + + +- Pass a `fish` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=fish <(cat <<'SUDO_EOF' +echo "What is your name?" +read -p "" name +echo "Hi, $name." +SUDO_EOF +) +``` +   + + + + +##### `python` + +Currently, `python` refers to `python3` and `python2` refers to `python2` in termux. Check [Termux Python Wiki](https://wiki.termux.com/wiki/Python) for more information. + +- Pass a `python` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=python <(cat <<'SUDO_EOF' +import sys +print("Hi, %s %s." % (sys.argv[1], sys.argv[2])) +SUDO_EOF +) "python" "shell" +``` + + + +- Pass a `python` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=python <(cat <<'SUDO_EOF' +name = input("What is your name?\n") +print("Hi, %s." % name) +SUDO_EOF +) +``` +  + + +*The following "tests" are in solidarity with the `youtube-dl` devs, EFF and the current Github response of the "incident".* + +The `youtube-dl` file is actually not a single python script text file but is a [binary file](https://github.com/ytdl-org/youtube-dl/blob/9fe50837c3e8f6c40b7bed8bf7105a868a7a678f/Makefile#L70) containing multiple python files. + +- Read `python` script text from a file using `cat` and pass it with process substitution. Passing the data of the `youtube-dl` file to `sudo` script using process substitution will engage automatic `base64` encoding of the data and creation of temp script file. The `youtube-dl` generates its help output based on the named of the its own file, hence `--script-name` is passed, otherwise help with contain `sudo_core_script` entries instead. The current size of the `youtube-dl` binary is over `1MB` and so its data cannot be passed as an argument directly (after `base64` encoding) since [Arguments Data Limits](#Arguments-and-Result-Data-Limits) will cross. + +``` +sudo -s --shell=python --script-name="youtube-dl" <(cat "$PREFIX/bin/youtube-dl") --help +``` + + +- Pass a path to `python` script file to the `python` shell instead of script text, with an arg. + +``` +sudo -sF --shell=python '$PREFIX/bin/youtube-dl' --help +``` + + +- Read `python` script text from a file using `cat` in a subshell and pass it as an argument. The script size must not cross [Arguments Data Limits](#Arguments-and-Result-Data-Limits). If the script contains binary or non `UTF-8` data, then pipe the output of `cat` to `base64` and also pass the `--script-decode` option. + +``` +sudo -s --shell=python "$(cat "$PREFIX/bin/bandcamp-dl")" --help +``` + +``` +sudo -s --script-decode --shell=python "$(cat "$PREFIX/bin/bandcamp-dl" | base64)" --help +``` +   + + + + +##### `ruby` + +- Pass a `ruby` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=ruby <(cat <<'SUDO_EOF' +puts "Hi, " + ARGV[0] + " " + ARGV[1] + "." +SUDO_EOF +) "ruby" "shell" +``` + + + +- Pass a `ruby` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=ruby <(cat <<'SUDO_EOF' +puts "What is your name?" +name = STDIN.gets +name = '' if name.nil? +puts "Hi, " + name.chomp.to_s + "." +SUDO_EOF +) +``` +   + + + + +##### `node` + +- Pass a `node` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=node <(cat <<'SUDO_EOF' +console.log(`Hi, ${process.argv[2]} ${process.argv[3]}.`) +SUDO_EOF +) "node" "shell" +``` + + + +- Pass a `node` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=node <(cat <<'SUDO_EOF' +const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout +}) + +readline.question(`What is your name?\n`, (name) => { + console.log(`Hi, ${name}.`) + readline.close() +}) +SUDO_EOF +) +``` +   + + + + +##### `perl` + +- Pass a `perl` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=perl <(cat <<'SUDO_EOF' +print "Hi, ", $ARGV[0], " ", $ARGV[1], ".\n"; +SUDO_EOF +) "perl" "shell" +``` + + + +- Pass a `perl` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=perl <(cat <<'SUDO_EOF' +print "What is your name?\n"; +$name = <STDIN>; +chomp($name); +print "Hi, ", $name, ".\n"; +SUDO_EOF +) +``` +   + + + + +##### `lua5.2` + +- Pass a `lua5.2` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=lua5.2 <(cat <<'SUDO_EOF' +io.write('Hi, ', arg[1], ' ', arg[2], '.\n') +SUDO_EOF +) "lua5.2" "shell" +``` + + + +- Pass a `lua5.2` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=lua5.2 <(cat <<'SUDO_EOF' +io.write('What is your name?\n') +local name = io.read() +io.write('Hi, ', name, '.\n') +SUDO_EOF +) +``` +   + + + + +##### `lua5.3` + +- Pass a `lua5.3` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=lua5.3 <(cat <<'SUDO_EOF' +io.write('Hi, ', arg[1], ' ', arg[2], '.\n') +SUDO_EOF +) "lua5.3" "shell" +``` + + + +- Pass a `lua5.3` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=lua5.3 <(cat <<'SUDO_EOF' +io.write('What is your name?\n') +local name = io.read() +io.write('Hi, ', name, '.\n') +SUDO_EOF +) +``` +   + + + + +##### `lua5.4` + +- Pass a `lua5.4` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=lua5.4 <(cat <<'SUDO_EOF' +io.write('Hi, ', arg[1], ' ', arg[2], '.\n') +SUDO_EOF +) "lua5.4" "shell" +``` + + + +- Pass a `lua5.4` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=lua5.4 <(cat <<'SUDO_EOF' +io.write('What is your name?\n') +local name = io.read() +io.write('Hi, ', name, '.\n') +SUDO_EOF +) +``` +   + + + + +##### `php` + +- Pass a `php` script text with process substitution that prints the first `2` args to `stdout`. + +``` +sudo -s --shell=php <(cat <<'SUDO_EOF' +<?php +echo "Hi, " . $argv[1] . " " . $argv[2] . ".\n"; +SUDO_EOF +) "php" "shell" +``` + + + +- Pass a `php` script text with process substitution that reads a name from `stdin` and prints it to `stdout`. + +``` +sudo -s --shell=php <(cat <<'SUDO_EOF' +<?php +echo "What is your name?\n"; +$name = readline(); +echo "Hi, " . $name . ".\n"; +SUDO_EOF +) +``` +## + + + +### Templates + +#### Tasker + +- `Tasks` + - `XML` + Download the [Termux Tasker Plugin Sudo Templates Task XML](templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.xml) and [Termux RUN_COMMAND Intent Sudo Templates Task XML](templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.xml) files to the android download directory. To download, right-click or hold the `Raw` button at the top after opening a file link and select `Download/Save link` or use `curl` from a termux shell. Then import the downloaded task files into Tasker by long pressing the `Task` tab button in Tasker home and selecting `Import Task`. + + `curl -L 'https://github.com/termux/termux-tasker/raw/master/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.xml' -o "/storage/emulated/0/Download/Termux_Tasker_Plugin_Sudo_Templates.tsk.xml"` + + `curl -L 'https://github.com/termux/termux-tasker/raw/master/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.xml' -o "/storage/emulated/0/Download/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.xml"` + + - `Taskernet` + Import `Termux Tasker Plugin Sudo Templates Task` from `Taskernet` from [here](https://taskernet.com/shares/?user=AS35m8mXdvaT1Vj8TwkSaCaoMUv220IIGtHe3pG4MymrCUhpgzrat6njEOnDVVulhAIHLi6BPUt1&id=Task%3ATermux+Tasker+Plugin+Sudo+Templates). + Import `Termux RUN_COMMAND Intent Sudo Templates Task` from `Taskernet` from [here](https://taskernet.com/shares/?user=AS35m8mXdvaT1Vj8TwkSaCaoMUv220IIGtHe3pG4MymrCUhpgzrat6njEOnDVVulhAIHLi6BPUt1&id=Task%3ATermux+RUN_COMMAND+Intent+Sudo+Templates). + + Check [Termux Tasker Plugin Sudo Templates Task Info](templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.md) and [Termux RUN_COMMAND Intent Sudo Templates Task Info](templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.md) files for more info on the tasks. + + +Termux needs to be granted `Storage` permission to allow it to access `/storage/emulated/0/Download` directory, otherwise you will get permission denied errors while running commands. +## + + + +### Passing Arguments + +The `core_script` or any other arguments passed for all the command types must be preserved in their original form and must be passed as is to `sudo` without any variable expansion or history expansion, etc. + +This can be done in two ways, either using single quotes to surround the `core_script` and arguments or passing them with process substitution with a literal `cat` `heredoc`. +  + +If you are using [Termux:Tasker] plugin to run `sudo` commands, you would need to use single quotes to pass arguments, since it doesn't support process substitution. You would need to install [Termux:Tasker] version `>= 0.5` since argument parsing is broken in older versions. Check the [Passing Arguments Surrounded With Single Quotes](#Passing-Arguments-Surrounded-With-Single-Quotes) section for more details. Check the `Template 2` and `Template 3` of the [Termux Tasker Plugin Sudo Templates Task](#Templates) task for templates on how to use single quotes to pass arguments with Tasker. Basically, just set your script text to the `%core_script` variable with the `Variable Set` action and add any additional command options or arguments to the `%arguments` variable. +  + +If you are using a foreground terminal session or scripts to run `sudo` commands, you can use single quotes to pass arguments or use process substitution. If you are using a foreground terminal session, then you must disable the `bash` command completion and history expansion for the current terminal session before running `sudo` command to pass multi-line arguments by running `bind 'set disable-completion on'; set +H`. Check the [Passing Arguments Using Process Substitution](#Passing-Arguments-Using-Process-Substitution) section for more details. Check the [Examples](#Examples) section for templates on how to use process substitution to pass arguments. +  + +If you are using [RUN_COMMAND Intent] to run `sudo` commands with Tasker using the `TermuxCommand()` function in `Tasker Function` action, you don't need to surround the `core_script` or arguments with single quotes, since arguments are split on a simple comma `,` instead. If your arguments themselves contain simple commas `,` (`U+002C`, `,`, `,`, `comma`), then you must replace them with the comma alternate character `‚` (`#U+201A`, `‚`, `‚`, `single low-9 quotation mark`) for each argument separately before passing them to the intent action and would also need to pass the `-r` command option to `sudo`. Check the [Passing Arguments Using RUN_COMMAND Intent](#Passing-Arguments-Using-RUN_COMMAND-Intent) section for more details. Check the `Template 2` of the [Termux RUN_COMMAND Intent Sudo Templates Task](#Templates) task for a template on how to replace commas in each argument separately with Tasker. +  + +If you are using [RUN_COMMAND Intent] to run `sudo` commands with Tasker or other apps using the `am` command, like using the `Run Shell` action in Tasker, you need to surround all your arguments, like the `core_script` and all other arguments with single quotes when passing them to the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra after you have escaped all the single quotes in the final value, since otherwise it may result in incorrect quoting if the arguments themselves contain single quotes. However, due to the string array extra, the arguments are still split on a simple comma `,` so if your arguments themselves contain simple commas `,` (`U+002C`, `,`, `,`, `comma`), then you would also have to replace them with the comma alternate character `‚` (`#U+201A`, `‚`, `‚`, `single low-9 quotation mark`) for each argument separately before passing them as the argument to the extra and would also need to pass the `-r` command option to `sudo`. Check the [Passing Arguments Using RUN_COMMAND Intent](#Passing-Arguments-Using-RUN_COMMAND-Intent) section for more details. Check the `Template 3` of the [Termux RUN_COMMAND Intent Sudo Templates Task](#Templates) task for a template on how to replace commas in each argument separately and also escape single quotes in all the arguments with Tasker. +  + +Note that for [RUN_COMMAND Intent], any arguments passed to any command options or the main arguments to `sudo` should also **not** be surrounded with single or double quotes to prevent whitespace splitting in the intent action, like done for usage with `Termux:Tasker` plugin since splitting will occur on simple comma characters instead. Check the `Template 4` of the [Termux RUN_COMMAND Intent Sudo Templates Task](#Templates) task for a template for this. +   + + + +##### Passing Arguments Surrounded With Single Quotes + +Any argument surrounded with single quotes is considered a literal string and variable expansion is not done. However, if an argument itself contains single quotes, then they will need to be escaped properly. You can escape them by replacing all single quotes `'` in an argument value with `'\''` **before** passing the argument surrounded with single quotes. So an argument surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed to `sudo`. + +For `Tasker`, you can use the `Variable Search Replace` action on an `%argument` variable to escape the single quotes. Set the `Search` field to one single quote `'`, and enable `Replace Matches` toggle, and set `Replace With` field to one single quote, followed by two backslashes, followed by two single quotes `'\\''`. The double backslash is to escape the backslash character itself. + +Escaping single quotes while running commands in a foreground terminal session will be much harder if there are many single quotes, same would apply for double quote surrounded strings, so use process substitution instead, check below. + +The format is the following + +``` +sudo -s ' +<core_script> +' '[some arg1]' '[some arg2]' +``` +   + + + +##### Passing Arguments Using Process Substitution + +[Process Substitution] can be used to pass the `core_script` and `core_script_args` for the `script` command type and to pass the `command_args` for the `path` command type when running `sudo` from a foreground terminal session or from a script. + +The following is the format for passing `core_script` text. + +``` +sudo -s <(cat <<'SUDO_EOF' +<core_script> +SUDO_EOF +) +``` + +The following is the process substitution part + +``` +<( + +) +``` + +Inside it there is a `cat` [Here Document]. The script text should start after a newline after the `'SUDO_EOF'` part. You can type anything as script text other than `SUDO_EOF` which when read, ends the script. The ending `SUDO_EOF` should be alone on a separate line. The starting `'SUDO_EOF'` is surrounded with single quotes so that script is considered a literal string and variable expansion, etc doesn't happen. + +``` +cat <<'SUDO_EOF' + +SUDO_EOF +``` + +Basically any text you type inside the `cat` heredoc will be passed to the process substitution which will create a temporary file descriptor for it in `/proc/self/fd/<n>` and pass the path to `sudo` script so that it can read the argument text from it. + +You can also read text from an existing script file using `cat` and pass that to `sudo` like the following + +``` +sudo -s <(cat "~/some-script") +``` +   + + + +##### Passing Arguments Using RUN_COMMAND Intent + +To use [RUN_COMMAND Intent] that has arguments working properly, you need to install Termux version `>= 0.100` and Tasker version `>= 5.9.4.beta`. However, leading and trailing whitespaces from arguments will be removed for Tasker version `< 5.11.1.beta` if you are using `TermuxCommand()` function, so its advisable to use a higher version or use `am` command instead. +  + +If you are using the `am` command, the format is `am startservice --user 0 -n com.termux/com.termux.app.RunCommandService -a com.termux.RUN_COMMAND --es com.termux.RUN_COMMAND_PATH '<path>' --esa com.termux.RUN_COMMAND_ARGUMENTS '<one_or_more_args_seperated_with_commas>' --es com.termux.RUN_COMMAND_WORKDIR '/data/data/com.termux/files/home' --ez com.termux.RUN_COMMAND_BACKGROUND 'false'` +  + +If you are using the `TermuxCommand()` function, the format is `TermuxCommand(path,<one_or_more_args_seperated_with_commas>,workdir,background)`. The args can be of any count, each separated with a simple comma `,`. The only condition is that in the list the first must be `path` and the last two must be `workdir` and `background` respectively. +  + +For both the `--esa com.termux.RUN_COMMAND_ARGUMENTS` string array extra and the `TermuxCommand()` function, if you want to pass an argument that itself contains a simple comma `,` (`U+002C`, `,`, `,`, `comma`), it must be escaped with a backslash `\,` so that the argument isn't split into multiple arguments. The only problem is that, the arguments received by the script being executed will contain `\,` instead of `,` since the reversal isn't done as described in the [am command source](https://android.googlesource.com/platform/frameworks/base/+/21bdaf1/cmds/am/src/com/android/commands/am/Am.java#572) while converting to a string array. Tasker uses the same method. There is also no way for the `am` command or the script to know whether `\,` was done to prevent arguments splitting or `\,` was a literal string naturally part of the argument. + +``` +// Split on commas unless they are preceeded by an escape. +// The escape character must be escaped for the string and +// again for the regex, thus four escape characters become one. +String[] strings = value.split("(?<!\\\\),"); +intent.putExtra(key, strings); +``` +  + +`sudo` uses an alternative method to handle such conditions. If an argument contains a simple comma `,`, then instead of escaping them with a backslash `\,`, replace all simple commas with the comma alternate character `‚` (`#U+201A`, `‚`, `‚`, `single low-9 quotation mark`). This way argument splitting will not be done. You can pass the `-r` option to `sudo` which will then parse arguments as per `RUN_COMMAND` intent rules to replace all the comma alternate characters back to simple commas. It would be unlikely for the `core_script` or the arguments to naturally contain the comma alternate characters for this to be a problem. Even if they do, they might not be significant for any script logic. If they are, then you can set a different character that should be replaced, with the `--comma-alternative` option. The `-r` and `--comma-alternative` options should ideally be the first options passed so that `sudo` replaces the alternative comma characters from all arguments passed after the options. + +For `Tasker` use the `Variable Search Replace` action on an `%argument` variable to replace the simple comma characters. Set the `Search` field to one simple comma `,`, and enable `Replace Matches` toggle, and set `Replace With` field to `%comma_alternative` where the `%comma_alternative` variable must contain the comma alternate character `‚`. +## + + + +### Issues + + +##### Automatic redirection of stderr to stdout in SuperSU + +In `SuperSU` `v2.82` for the `script` command type, if `stdin` is available like running `su` in an interactive shell like from a foreground terminal session, then it automatically redirects `stderr` of commands to `stdout`, specially affecting the `--script-redirect` and related command options. However, if commands are run in a non-interactive shell, in the background, like from `Termux:Tasker` plugin, then `stdout` and `stderr` streams behave normally and are separate. This can be confirmed by running `(su -c 'echo 1 1>&2' 2>/dev/null)` and `(exec <&-; su -c 'echo 1 1>&2' 2>/dev/null)` in a terminal session. In the former case, `1` is still printed on the screen even though `stderr` is redirected to `/dev/null`. The later case closes the `stdin` file descriptor which makes `su` assume its running non-interactively. Running `(su -c 'echo 1 1>&2' 1>/dev/null)` also suppresses printing since it redirects `stdout` to `/dev/null` instead. Running `(bash -c 'echo 1 1>&2' 2>/dev/null)` works normally. Reopening `stdin` with hacks, inside the `su` shell doesn't work either for a few reasons, including that `stderr` redirection to `stdout` starts happening again. This seems to be an issue of the [libsuperuser](https://github.com/Chainfire/libsuperuser/blob/v1.1.0/libsuperuser/src/eu/chainfire/libsuperuser/Shell.java) itself or how the `su` binary handles streams internally and might not be solvable but if you have a solution that can be used to prevent automatic redirection, please report it. + +This does not affect usage with `Termux:Tasker` in background mode. This does not apply to `Magisk`, at least the currently latest version `v21.1`. However, this may apply to other `su` implementation. +   + + + +##### su -c support + +The `sudo` script requires the `-c` command option support by the `su` binary. The `su` that comes with the android studio `avd` does not support it. Other `su` implementations may not support it either. + +Moreover, linux distros removed support for starting interactive shells with the `su -c` command. Check [debian su manpage](https://manpages.debian.org/testing/login/su.1.en.html). The `-c` command option info specifies that `The executed command will have no controlling terminal. This option cannot be used to execute interactive programs which need a controlling TTY.`. For more info, check [debian bug report #628843](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=628843) and [ubuntu CVE-2005-4890](https://ubuntu.com/security/CVE-2005-4890). However, this likely does not apply to android `su` implementations, at least does not apply for `SuperSU` and `Magisk` currently, so `sudo` should work fine, at least on those. +## + + + +### Worthy Of Note + + +#### RC File Variables + +If you don't know what `$PATH`, `$LD_LIBRARY_PATH` and `$PS1` variables or `rc` files are or don't care to find out, then just run the commands below so that `sudo` works properly, otherwise read the details below. Ignore `No such file or directory` errors when running the commands. + +``` +sed -i'' -E 's/^PS1='\''\\\$ '\''$/\(\[ -z "\$PS1" \] \|\| \[\[ "\$PS1" == '\''\\s-\\v\\\$ '\'' \]\]\) \&\& PS1='\''\\\$ '\''/' "/data/data/com.termux/files/usr/etc/bash.bashrc" +sed -i'' -E 's/^PS1='\''%# '\''$/\(\[ -z "\$PS1" \] \|\| \[\[ "\$PS1" == '\''%m%# '\'' \]\]\) \&\& PS1='\''%# '\''/' "/data/data/com.termux/files/usr/etc/zshrc" +su -c "rm \"/data/data/com.termux/files/home/.suroot/.bashrc\"" +``` +  + + +`rc` files, short for `run commands`, are shell specific files that are run or sourced whenever an interactive shell is started to define variables and functions etc. + +Make sure in your `rc` files like `~/.suroot/.bashrc` file for `bash` (where `~/.suroot` is the `sudo shell` home), the `$PATH`, `$LD_LIBRARY_PATH` and `$PS1` variables and any other variables exported by `sudo` are not overridden, **otherwise the `sudo` commands will not work properly**, since `sudo` exports its own custom variable values which will get overridden when `rc` files are sourced by any new shell started. You can run commands like `sudo -v --dry-run --shell=bash su` to see what variables are normally exported for a given shell by `sudo`. +  + + +##### `$PATH` or `$LD_LIBRARY_PATH` + +Check the [PATH and LD_LIBRARY_PATH Priorities](#path-and-ld_library_path-priorities) section for more info on what these variables are. + +For the `$PATH` or `$LD_LIBRARY_PATH` variables, either remove their variable set lines completely or if necessary only append any required paths to the existing variable values like `export PATH="$PATH:/dir1:/dir2"` and `export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/dir1:/dir2"` instead of overriding them with `export PATH="/dir1:/dir2"` and `export LD_LIBRARY_PATH="/dir1:/dir2"` in your `rc` files. +  + + +##### `$PS1` + +The `$PS1` variable, short for [Prompt String 1], defines the characters you see at the start of the "line" when typing commands in shells running interactively like `bash` or `zsh`. For `termux` `bash` shell, this defaults to `$ `. For `termux` `zsh` shell, this defaults to `% `. + +If you want to allow `sudo` to set its own default `$PS1` value `# ` or the one set with the `$SUDO_SHELL_PS1` or `$SUDO_POST_SHELL_PS1` variables in the `sudo.config` file, then **make sure the `$PS1` value is not overridden by the shell `rc` files, otherwise you will not be able to easily tell the difference between whether you are running a shell via `sudo` or normally as the `termux` user when you run commands like `sudo su`.** You can however check the value of `$SHLVL` to see the nested shell level, run `printenv | grep SHLVL`. + +1. The `rc` files in `$PREFIX/etc/` are sourced first whenever shells are started by `sudo`. The `$PS1` value is set and exported by `sudo` before new shells are started, however, the value will get replaced if its overridden by the `rc` files when they are sourced during startup of the new shell. + + - `bash` shell currently uses the `$PREFIX/etc/bash.bashrc` file, in which it sets the default value of `$PS1` to `\$ `. You need to replace the line `PS1='\$ '` with the conditional `([[ -z "$PS1" ]] || [[ "$PS1" == '\s-\v\$ ' ]]) && PS1='\$ '` so that `$PS1` is only set if its not already set or is set to the default value internally used by `bash`. You can run the command `sed -i'' -E 's/^PS1='\''\\\$ '\''$/\(\[ -z "\$PS1" \] \|\| \[\[ "\$PS1" == '\''\\s-\\v\\\$ '\'' \]\]\) \&\& PS1='\''\\\$ '\''/' "/data/data/com.termux/files/usr/etc/bash.bashrc"` to automatically do it or you can do it manually by running `nano "/data/data/com.termux/files/usr/etc/bash.bashrc"`. + + - `zsh` shell currently uses the `$PREFIX/etc/bash.zshrc` file, in which it sets the default value of `$PS1` to `%# `. You need to replace the line `PS1='%# '` with the conditional `([[ -z "$PS1" ]] || [[ "$PS1" == '%m%# ' ]]) && PS1='%# '` so that `$PS1` is only set if its not already set or is set to the default value internally used by `zsh`. You can run the command `sed -i'' -E 's/^PS1='\''%# '\''$/\(\[ -z "\$PS1" \] \|\| \[\[ "\$PS1" == '\''%m%# '\'' \]\]\) \&\& PS1='\''%# '\''/' "/data/data/com.termux/files/usr/etc/zshrc"` to automatically do it or you can do it manually by running `nano "/data/data/com.termux/files/usr/etc/zshrc"`. + +2. The `rc` files in `~/.suroot` (default `sudo shell` home) are also sourced afterwards whenever shells are started by `sudo`. They must also not override the `$PS1` value. However, if you want to set a custom value in them for usage outside `sudo`, then you can add conditionals to override `$PS1` only if its not already set or is set to the default `termux` value set by `$PREFIX/etc/*` `rc` files. + + - `bash` shell default `rc` file set by `sudo` is `~/.suroot/.bashrc`. You can for example set `$PS1` to `£ ` by adding the line `([[ -z "$PS1" ]] || [[ "$PS1" == '\$ ' ]]) && PS1='£ '` to it. + + - `zsh` shell default `rc` file set by `sudo` is `~/.suroot/.zshrc`. You can for example set `$PS1` to `£ ` by adding the line `([[ -z "$PS1" ]] || [[ "$PS1" == '%# ' ]]) && PS1='£ '` to it. + +This will ensure that the exported `$PS1` variables will not be overridden by `rc` files for `bash` and `zsh`. For other shells that may use the `$PS1` variable, you can use similar conditionals in their `rc` files. +  + + +##### `termux-sudo by st42` +If you were **previously using [termux-sudo by st42]**, then it would have automatically created the `~/.suroot/.bashrc` file with entries like the following. You should either remove those lines if you haven't exported custom values yourself or remove the file entirely if you haven't made changes to it yourself. + +``` +export LD_LIBRARY_PATH=$PRE/usr/lib +export PATH=/data/data/com.termux/files/usr/bin:/data/data/com.termux/files/usr/bin/applets:/system/xbin:/system/bin +export PS1="# " +``` + +To remove the `~/.suroot/.bashrc` file, run `su -c "rm \"/data/data/com.termux/files/home/.suroot/.bashrc\""` command. + +To edit the `~/.suroot/.bashrc` file, run `su -c "nano \"/data/data/com.termux/files/home/.suroot/.bashrc\""` command. Make changes as advised above, then press `Ctrl+o` and then `Enter` to save and `Ctrl+x` to exit. You can also open the file with a text editor app with root support like [QuickEdit] or [QuickEdit Pro]. +   + + + +#### Arguments and Result Data Limits + +There are limits on the arguments size you can pass to commands or the full command string length that can be run, which is likely equal to `131072` bytes or `128KB` for an android device defined by `ARG_MAX` but after subtracting shell environment size, etc, it will roughly be around `120-125KB` but limits may vary for different android versions and kernels. You can check the limits for a given termux session by running `true | xargs --show-limits`. If you exceed the limit, you will get exceptions like `Argument list too long`. You can manually cross the limit by running something like `$PREFIX/bin/echo "$(head -c 131072 < /dev/zero | tr '\0' 'a')" | tr -d 'a'`, use full path of `echo`, otherwise the `echo` shell built-in will be called to which the limit does not apply since `exec` is not done. + +Moreover, exchanging data between `Tasker` and `Termux:Tasker` is done using [Intents](https://developer.android.com/guide/components/activities/parcelables-and-bundles), like sending the command and receiving result of commands in `%stdout` and `%stderr`. However, android has limits on the size of *actual* data that can be sent through intents, it is roughly `500KB` on android `7` but may be different for different android versions. + +Basically, make sure any data/arguments you pass to `sudo` script directly on the shell or through scripts or using the `Termux:Tasker` plugin or [RUN_COMMAND Intent] intent is less than `120KB` (or whatever you found) and any expected result sent back if using the `Termux:Tasker` plugin is less than `500KB`, but best keep it as low as possible for greater portability. If you want to exchange an even larger data between tasker and termux, use physical files instead. + +The argument data limits also apply for the [RUN_COMMAND Intent] intent. +   + + + +#### PATH and LD_LIBRARY_PATH Priorities + +The word executable will be used henceforth for binaries, scripts and any other executable files. + +`Termux` executables currently exist at `/data/data/com.termux/files/usr/bin` and `/data/data/com.termux/files/usr/bin/applets` `Termux` libraries exist at `/data/data/com.termux/files/usr/lib`. + +`Android` executables normally exist at `/system/bin` and/or `/system/xbin` `Android` libraries exist at `/system/lib` and/or `/system/lib64`. + +When `sudo su` commands is run, then the termux executables paths are prepended to android executables paths in the `$PATH` variable. The termux library paths are prepended to android library paths in the `$LD_LIBRARY_PATH` variable. This gives priority to termux paths. + +When `sudo asu` commands is run, then the android executables paths are prepended to termux executables paths in the `$PATH` variable. The android library paths are prepended to termux library paths in the `$LD_LIBRARY_PATH` variable. This gives priority to android paths. + +The `$PATH` variable sets the paths to search for executables when commands are executed. The `$LD_LIBRARY_PATH` variable sets the paths to search for libraries for dynamic linking required by the commands that are executed. The path that appears first in both the variables is searched first and if the required binary, executable or library is found, that is the one thats used without looking further. +  + +There are a few important things to consider when using `sudo` with termux. + +A executable that you want to run may exist in both termux and android executable paths but you may want to run a specific one. If you want to run the termux one instead of the the android one then run `sudo su` command and then run the command to run the executable. If you want to run the android one instead of the the termux one then run `sudo asu` command and then run the command to run the executable. However in both cases, if you write the absolute path of the executable instead of just writing its basename, the executable at the path you wrote will be executed even if the other ones path exist before in the `$PATH` variable. + +Another thing to consider is that dynamic library linking errors may occur when executables try to link to the wrong library. Executables should be linked with libraries they are compatible with and that define all the needed functions needed by the executable. Executables that exist in termux executable path should ideally be linked with libraries that exist in termux library path. Executables that exist in android executable path should ideally be linked with libraries that exist in android library path. + +When an executable is run, the paths in the `$LD_LIBRARY_PATH` variable are searched for the library that is required and whichever matching library is found first is used, even if that library is not compatible with the executable. If the library is indeed incompatible a linking error occurs with errors that may include words like `CANNOT LINK EXECUTABLE` and `cannot locate symbol some_symbol referenced by /lib....`. + +So if an executable in android executable path tries to link with a library in termux library paths to which it is incompatible with, then a linking error will occur. This is likely to happen with some executables including the android `dumpsys` or `input` binaries among others. A linking error may occur the other way around too, when a termux executable tries to link with libraries in android library path. To prevent these linking error from occurring in most situations, separate `sudo su` and `sudo asu` commands exist, which set the correct order of paths in the `$PATH` and `$LD_LIBRARY_PATH` variables so that normally termux executables are linked with termux libraries and android executables are linked with android libraries whenever either command is run. +  + +However, another way to automatically prioritize android libraries is by running the `path` command type with `sudo <command>`. If the `command` exists in the `/system` partition, then android library paths are prepended to termux library paths in the `$LD_LIBRARY_PATH` variable automatically. An absolute path is not needed to be passed for this to work as the `$PATH_TO_EXPORT` is automatically searched. This is helpful for situations when you are already in a `sudo su` shell and do not want to shift to the `sudo asu` shell or unset `$LD_LIBRARY_PATH` to run just one executable in android executables paths. + +You can also use the `tpath` and `apath` functions if they are defined in the `rc` file of your interactive shell to shift priorities. + +Normally `sudo su` will work fine without problem when dropping to `sudo` shell. But if you want to specifically run an executable in the android executable paths instead of the one in termux executable paths or are getting linking errors when running android executables with `sudo su`, then try using `sudo asu` and then running the required command or use `sudo <command>` or `sudo -a <command>`. + + + +#### `tpath` and `apath` functions + +For the shells `bash zsh dash sh fish ksh`, additional functions named `tpath` and `apath` are added to their `rc` files if the `sudo` script creates the `rc` files, they are not added otherwise. You can call these functions to set priorities from inside interactive shell sessions only when running `sudo su`, `sudo asu` or `sudo -is <core_script` commands, since they depend on some environmental variables set by the `sudo` script and are not hard-coded in case of future changes. + +The `tpath` function will set priority to termux bin and library paths in `$PATH` and `$LD_LIBRARY_PATH` variables. + +The `apath` function will set priority to android bin and library paths in `$PATH` and `$LD_LIBRARY_PATH` variables. + +The functions allows the users to quickly switch priorities without having to switch between `sudo su` and `sudo asu` shells. + +If you already have an existing `rc` file for your shell like `~/.suroot/.bashrc` and want to add the functions to it. Just temporarily move (not copy) the file to somewhere else and run `sudo su` command with the optional `--shell` option, then copy the functions from the new `rc` file created by `sudo` to your old file, then remove the new file and move the old file back. + + + +#### `export` and `unset` functions + +For the `fish` shell, additional functions named `export` and `unset` are also added to the `rc` files if the `sudo` script creates its `rc` file, they are not added otherwise. The functions port the `bash` `export var=value` and `unset var` functionality respectively. +## + + + +### Tests + +Check the [sudo_tests](tests/sudo_tests) script to run automated tests for the `sudo` script command types, options and shells. Usage instructions are inside the script. There are more examples for running `sudo` inside the `sudo_tests` script that can be used by users, although may not be too user friendly to view or understand. +## + + + +### FAQs And FUQs + +Check [FAQs_And_FUQs.md](FAQs_And_FUQs.md) file for the **Frequently Asked Questions(FAQs)** and **Frequently Unasked Questions(FUQs)**. +## + + + +### Changelog + +Check [CHANGELOG.md](CHANGELOG.md) file for the **Changelog**. +## + + + +### Contributions + +`-` +## + + + +### Credits + +- [termux-sudo by st42] +- [tsu by cswl] +## + + + +### Donations + +- To donate money to support me, you can visit [here](https://github.com/agnostic-apollo/rand/blob/master/General/Donations.md) for more info. +## + +[Tasker App]: https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm +[Termux App]: https://github.com/termux/termux-app +[Termux:Tasker]: https://github.com/termux/termux-tasker +[QuickEdit]: https://play.google.com/store/apps/details?id=com.rhmsoft.edit +[QuickEdit Pro]: https://play.google.com/store/apps/details?id=com.rhmsoft.edit.pro +[Acode editor]: https://github.com/deadlyjack/code-editor +[Turbo Editor]: https://github.com/vmihalachi/turbo-editor + +[SuperSU]: https://forum.xda-developers.com/t/beta-2017-10-01-supersu-v2-82-sr5.2868133/ +[Magisk]: https://github.com/topjohnwu/Magisk + +[tudo]: https://github.com/agnostic-apollo/tudo +[RUN_COMMAND Intent]: https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/RunCommandService.java +[termux-sudo by st42]: https://gitlab.com/st42/termux-sudo +[tsu by cswl]: https://github.com/cswl/tsu + +[Process Substitution]: https://en.wikipedia.org/wiki/Process_substitution +[Here Document]: https://en.wikipedia.org/wiki/Here_document +[Prompt String 1]: https://www.cyberciti.biz/tips/howto-linux-unix-bash-shell-setup-prompt.html diff --git a/sudo b/sudo new file mode 100755 index 0000000..61fbef7 --- /dev/null +++ b/sudo @@ -0,0 +1,5958 @@ +#!/data/data/com.termux/files/usr/bin/bash + +#title: sudo +#description: a wrapper script to drop to the supported shells or +# execute shell script files or their text passed as +# an argument with superuser (root) context in termux +#author: agnostic-apollo +#usage: run "sudo --help" for detailed list of usages +#date: 14-Dec-2020 +#bash version: 4.1 or higher +#credits: https://gitlab.com/st42/termux-sudo +# https://github.com/cswl/tsu +version=0.1.0 + + + +### Install Instructions For Termux In Android: + +#The `sudo` file should be placed in termux `bin` directory `/data/data/com.termux/files/usr/bin`. +#It should have `termux` `uid:gid` ownership and have executable `700` permission before it can be run directly without `bash`. +# +#1. Download the `sudo` file. +# +# - Download to termux bin directory directly from github using `curl` using a non-root termux shell. +# Run `pkg install curl` to install `curl` first. +# - Latest release: +# +# `curl -L 'https://github.com/agnostic-apollo/sudo/releases/latest/download/sudo' -o "/data/data/com.termux/files/usr/bin/sudo"` +# +# - Specific release: +# +# `curl -L 'https://github.com/agnostic-apollo/sudo/releases/download/v0.1.0/sudo' -o "/data/data/com.termux/files/usr/bin/sudo"` +# +# - Master Branch *may be unstable*: +# +# `curl -L 'https://github.com/agnostic-apollo/sudo/raw/master/sudo' -o "/data/data/com.termux/files/usr/bin/sudo"` +# +# - Download `sudo` file manually from github to the android download directory and then copy it to termux bin directory. +# +# You can download the `sudo` file from a github release from the `Assets` dropdown menu. +# +# You can also download it from a specific github branch/tag by opening the `sudo` file from the `Code` section. +# Right-click or hold the `Raw` button at the top and select `Download/Save link`. +# +# Then copy the file to termux bin directory using `cat` command below or use a root file browser to manually place it. +# +# `cat "/storage/emulated/0/Download/sudo" > "/data/data/com.termux/files/usr/bin/sudo"` +# +#2. Set `termux` ownership and executable permissions. +# +# - If you used a `curl` or `cat` to copy the file, then use a non-root termux shell to set ownership and permissions with `chown` and `chmod` commands respectively: +# +# `export termux_bin_path="/data/data/com.termux/files/usr/bin"; export owner="$(stat -c "%u" "$termux_bin_path")"; chown "$owner:$owner" "$termux_bin_path/sudo" && chmod 700 "$termux_bin_path/sudo";` +# +# - If you used a root file browser to copy the file, then use `su` to start a root shell to set ownership and permissions with `chown` and `chmod` commands respectively: +# +# `export termux_bin_path="/data/data/com.termux/files/usr/bin"; export owner="$(stat -c "%u" "$termux_bin_path")"; su -c "chown \"$owner:$owner\" \"$termux_bin_path/sudo\" && chmod 700 \"$termux_bin_path/sudo\"";` +# +# - Or manually set them with your root file browser. You can find `termux` `uid` and `gid` by running the command `id -u` in a non-root termux shell or by checking the properties of the termux `bin` directory from your root file browser. +# + +sudo_set_default_variables() { + +#set termux and android default variables +TERMUX_FILES="/data/data/com.termux/files" +TERMUX_HOME_BASENAME="home" +TERMUX_HOME="$TERMUX_FILES/$TERMUX_HOME_BASENAME" +TERMUX_PREFIX_BASENAME="usr" +TERMUX_PREFIX="$TERMUX_FILES/$TERMUX_PREFIX_BASENAME" +TERMUX_BIN="$TERMUX_PREFIX/bin" +TERMUX_BIN_APPLETS="$TERMUX_PREFIX/bin/applets" +TERMUX_PATH="$TERMUX_BIN:$TERMUX_BIN_APPLETS" +TERMUX_LD_LIBRARY_PATH="$TERMUX_PREFIX/lib" +TMPDIR="$TERMUX_PREFIX/tmp" +SYS_XBIN="/system/xbin" +SYS_BIN="/system/bin" +ANDROID_PATH="$SYS_XBIN:$SYS_BIN" +ANDROID_OTHER_BIN_PATHS="/sbin:/vendor/bin:/system/sbin" +BASH_SHELL_PATH="$TERMUX_BIN/bash" +TERM="xterm-256color" + +#termux is currently an english-only environment +LANG="en_US.UTF-8" + + +### Set User Modifiable Variables Start +### It is highly advisable to use the sudo.config file instead to modify the default values + +#set SUDO_SHELL_HOME which will be used as sudo shell home directory +[ ! -z "$SUDO_SHELL_HOME" ] && custom_sudo_shell_home_set=1 +SUDO_SHELL_HOME="${SUDO_SHELL_HOME:=$TERMUX_HOME/.suroot}" #default to '$TERMUX_HOME/.suroot' +#SUDO_SHELL_HOME="${SUDO_SHELL_HOME:=$TERMUX_HOME}" #use '$TERMUX_HOME' as sudo shell home + +#set SUDO_POST_SHELL_HOME which will be used as sudo post shell home directory +[ ! -z "$SUDO_POST_SHELL_HOME" ] && custom_sudo_post_shell_home_set=1 +SUDO_POST_SHELL_HOME="${SUDO_POST_SHELL_HOME:=$TERMUX_HOME/.suroot}" #default to '$TERMUX_HOME/.suroot' +#SUDO_POST_SHELL_HOME="${SUDO_POST_SHELL_HOME:=$TERMUX_HOME}" #use '$TERMUX_HOME' as sudo post shell home + +#set the prompt string 1 for the sudo shell +SUDO_SHELL_PS1="${SUDO_SHELL_PS1:=# }" #default to "# " + +#set the prompt string 1 for the sudo post shell +SUDO_POST_SHELL_PS1="${SUDO_POST_SHELL_PS1:=# }" #default to "# " + +#set any additional paths you want to export other than termux bin, android bin and su bin paths, +#separated with colons `:` +#the string must not start or end with or contain two consecutive colons ':' +#the `--export-paths` option will override this value +ADDITIONAL_PATHS_TO_EXPORT="${ADDITIONAL_PATHS_TO_EXPORT:=}" #default to none + +#set to "1" or pass "-L" as argument if you want to automatically export all the paths that already exist +#in the PATH variable at the moment the sudo command is run +#the default paths mentioned above and any path in ADDITIONAL_PATHS_TO_EXPORT are always exported +export_all_existing_paths_in_path_variable="${export_all_existing_paths_in_path_variable:=0}" #default to 0 + +#set any additional ld library paths you want to export other than termux lib and android lib paths, +#separated with colons `:` +#the string must not start or end with or contain two consecutive colons ':' +#the `--export-ld-lib-paths` option will override this value +ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT="${ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT:=}" #default to none + +#set to "1" or pass "-P" as argument if you want to automatically export all the paths that already exist +#in the LD_LIBRARY_PATH variable at the moment the sudo command is run +#the default paths mentioned above and any path in ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT are always exported +export_all_existing_paths_in_ld_library_path_variable="${export_all_existing_paths_in_ld_library_path_variable:=0}" #default to 0 + +#set to 1 if you want to automatically create rc files for interactive shells, otherwise 0 +sudo_shells_automatically_create_rc_files="${sudo_shells_automatically_create_rc_files:=1}" #default to 1 + +#set to 1 if you want to automatically create history files for interactive shells, otherwise 0 +sudo_shells_automatically_create_history_files="${sudo_shells_automatically_create_history_files:=1}" #default to 1 + +#set to 1 if you want to store command history for interactive shells, otherwise 0 +sudo_shells_history_enabled="${sudo_shells_history_enabled:=1}" #default to 1 + + +#for f in SUDO_SHELL_HOME SUDO_POST_SHELL_HOME SUDO_SHELL_PS1 SUDO_POST_SHELL_PS1 ADDITIONAL_PATHS_TO_EXPORT export_all_existing_paths_in_path_variable ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT export_all_existing_paths_in_ld_library_path_variable sudo_shells_automatically_create_rc_files sudo_shells_automatically_create_history_files sudo_shells_history_enabled; do echo "$f=\`${!f}\`"; done; echo $'\n' + +### Set User Modifiable Variables End + + +### Set Default Variables Start +#The following variables must not be modified unless you know what you are doing + +sudo_verbose_level=0 #default to log level 0 +sudo_args_verbose_level=0 #set this to 1 manually, if you want to debug arguments received +rootfs_previous_state_ro=0 #default to yes +system_previous_state_ro=0 #default to yes + +command_type="path" #default to path +command_type_path_forced=0 #default to 0 +same_sudo_post_shell_as_sudo_shell=0 #default to 0 +same_sudo_post_shell_home_as_sudo_shell_home=0 #default to 0 +parse_commands_as_per_run_command_intent_rules=0 #default to 0 +exec_sudo_shell=0 #default to 0 +disable_preserve_environment_for_su=0 #default to 0 +disable_stdin_for_core_script=0 #default to 0 +run_core_script_in_background=0 #default to 0 +exit_early_if_core_script_fails=0 #default to 0 +go_back_to_last_activity_after_running_core_script=0 #default to 0 +go_to_launcher_activity_after_running_core_script=0 #default to 0 +clear_shell_after_running_core_script=0 #default to 0 +run_interactive_post_sudo_shell_after_running_core_script=0 #default to 0 +force_use_temp_script_file_for_core_script=0 #default to 0 +core_script_is_path_to_script_file=0 #default to 0 +decode_core_script_content=0 #default to 0 +do_not_delete_sudo_temp_directory_on_exit=0 #default to 0 +remove_previous_sudo_temp_files=0 #default to 0 +use_root_for_path_search_and_validation=0 #default to 0 +do_not_remount_partitions_back_to_ro_after_sudo=0 #default to 0 +force_remount_partitions_back_to_ro_after_sudo=0 #default to 0 +hold_after_sudo=0 #default to 0 +hold_only_on_failure=0 #default to 0 +sleep_after_sudo=0 #default to 0 +sleep_only_on_failure=0 #default to 0 +dry_run_sudo=0 #default to 0 +disable_arguments_logging=0 #default to 0 +set_sudo_shell_terminal_title=0 #default to 0 +force_set_priority_to_android_paths=0 #default to 0 + +SUDO_SHELL="" #default to none +SUDO_SHELL_BASENAME="" #default to none +SUDO_SHELL_PARENT_DIR="" #default to none +SUDO_POST_SHELL="" #default to none +SUDO_POST_SHELL_BASENAME="" #default to none +SUDO_POST_SHELL_PARENT_DIR="" #default to none +SUDO_SHELL_RCFILE="" #default to none +SUDO_POST_SHELL_RCFILE="" #default to none +SUDO_SHELL_HISTFILE="" #default to none +SUDO_POST_SHELL_HISTFILE="" #default to none +SUDO_TEMP_DIRECTORY="" #default to none +SUDO_TEMP_DIRECTORY_PREFIX=".sudo.temp" +SUDO_CORE_SCRIPT_TEMP_FILENAME="" #default to none +SUDO_CORE_SCRIPT_REDIRECT_MODE="" #default to none +SUDO_SHELL_WORKING_DIR="" #default to none +SUDO_SHELL_TERMINAL_TITLE="" #default to none +ADDITIONAL_SUDO_SHELL_PRE_COMMANDS_TO_RUN="" #default to none +ADDITIONAL_SUDO_SHELL_POST_COMMANDS_TO_RUN="" #default to none +ADDITIONAL_SUDO_POST_SHELL_PRE_COMMANDS_TO_RUN="" #default to none +ADDITIONAL_SUDO_POST_SHELL_POST_COMMANDS_TO_RUN="" #default to none +SUDO_SHELL_STDIN_STRING="" #default to none +SUDO_POST_SHELL_STDIN_STRING="" #default to none +SUDO_SUPPORTED_SHELLS="" #default to none +SUDO_SUPPORTED_POST_SHELLS="" #default to none +SLEEP_TIME_AFTER_SUDO="" #default to none +HOLD_STRING_AFTER_SUDO="" #default to none +COMMA_ALTERNATIVE="‚" #U+201A, ‚, ‚, single low-9 quotation mark +SUDO_ARG_MAX_SAFE_LIMIT=122880 #value should be in bytes, default to 120KB + +SUDO_SUPPORTED_INTERACTIVE_SHELLS="bash zsh dash sh fish python ruby pry node perl lua5.2 lua5.3 lua5.4 php python2 ksh" +SUDO_SUPPORTED_SCRIPT_SHELLS="bash zsh dash sh fish python ruby node perl lua5.2 lua5.3 lua5.4 php python2 ksh" +SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT="dash sh node php" + +declare -ga SUDO_COMMAND=() +declare -ga SHELL_COMMAND=() +declare -ga SHELL_INTERACTIVE_COMMAND=() +declare -ga SUDO_SHELL_COMMAND=() +declare -ga SUDO_SHELL_INTERACTIVE_COMMAND=() +declare -ga SUDO_POST_SHELL_INTERACTIVE_COMMAND=() + +declare -ga SHELL_ADDITIONAL_COMMAND_OPTIONS=() +declare -ga SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=() +declare -ga SUDO_SHELL_ADDITIONAL_COMMAND_OPTIONS=() +declare -ga SUDO_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=() +declare -ga SUDO_POST_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=() +declare -ga SUDO_RLWRAP_ADDITIONAL_COMMAND_OPTIONS=() +declare -ga SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS=() +declare -ga SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS=() + +#set regexes for validation +valid_number_regex='^[0-9]+$' +valid_absolute_path_regex='^(/[^/]+)+$' +valid_path_in_rootfs_partition_regex='^/[^/]*/?$' +valid_path_in_system_partition_regex='^/system/.*' +valid_su_mount_master_option_prefixed_string='^[ \t]*(--mount-master|-mm)[ \t]*' + +### Set Default Variables + +} + +[[ x"${BASH_SOURCE[0]}" == x"$0" ]] && sudo_exit_command="exit" || sudo_exit_command="return" + +function sudo_log() { local log_level="${1}"; shift; if [[ $sudo_verbose_level -ge $log_level ]]; then echo "$@"; fi } +function sudo_log_literal() { local log_level="${1}"; shift; if [[ $sudo_verbose_level -ge $log_level ]]; then echo -e "$@"; fi } +function sudo_log_errors() { echo "$@" 1>&2; } +function sudo_log_args() { if [[ $sudo_args_verbose_level -ge "1" ]]; then echo "$@"; fi } +function sudo_log_arg_errors() { echo "$@" 1>&2; } + +#enable the following line if you want to log function runtimes with the '-vv' option +#sudo_start_time=$(date +%s%3N); function sudo_log_literal() { local log_level="${1}"; shift; if [[ $sudo_verbose_level -ge $log_level ]]; then if [[ "$*" == *"Running"* ]]; then sudo_end_time=$(date +%s%3N); run_time=$((sudo_end_time-sudo_start_time)); echo -e "Runtime $run_time\n"; sudo_start_time=$(date +%s%3N); fi; echo -e "$@"; fi } + + +sudo_main() { + + local return_value + + #if sudo_config_file exists, source it + sudo_config_file="/data/data/com.termux/files/home/.config/sudo/sudo.config" + sudo_config_file_sourced=0 + if [ -f "$sudo_config_file" ] && [ -r "$sudo_config_file" ]; then + source "$sudo_config_file" + sudo_config_file_sourced=1 + fi + + #set default variables + #there are set by a function so that they do not automatically override already set variables + #if the script is sourced + #user modifiable variables will only be set if the sudo_config_file didn't set them or + #if they were not already exported + sudo_set_default_variables + + #process the command or options passed to sudo + process_sudo_parameters "$@" + + #validate command_type + if [ -z "$command_type" ]; then + show_sudo_help + return_value=0 + elif [[ "$command_type" != *,* ]] && [[ ",su,asu,path,script," == *",$command_type,"* ]]; then + sudo_run + return_value=$? + else + sudo_log_errors "Unknown command type $command_type" + exit_sudo_on_error + fi + + sudo_run_pre_exit_commands $return_value + + $sudo_exit_command $return_value + +} + +sudo_run() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo" + + #if sudo_config_file_sourced is enabled + if [[ "$sudo_config_file_sourced" == "1" ]]; then + sudo_log 2 "config was sourced from ~/.config/sudo/sudo.config" + fi + + SUDO_SCRIPT_PATH="$(readlink -f -- "${BASH_SOURCE[0]}")" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$SUDO_SCRIPT_PATH" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "Failure while finding SUDO_SCRIPT_PATH" + sudo_log_errors "SUDO_SCRIPT_PATH=\"$SUDO_SCRIPT_PATH\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + export SUDO_SCRIPT_PATH + + #if command_type equals "su" or "asu" + if [[ "$command_type" == "su" ]] || [[ "$command_type" == "asu" ]]; then + SUDO_SUPPORTED_SHELLS="$SUDO_SUPPORTED_INTERACTIVE_SHELLS" + + # The arguments passed to '--shell-options' are meant for interactive shell + SUDO_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=("${SUDO_SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + SUDO_SHELL_ADDITIONAL_COMMAND_OPTIONS=() + + interactive_sudo_shell_required=1 + #if command_type equals "path" + elif [[ "$command_type" == "path" ]]; then + SUDO_SUPPORTED_SHELLS="bash" + + #unset SUDO_SHELL so that it defaults to bash in sudo_set_sudo_shell + SUDO_SHELL="" + + #ignore any shell options that are passed + SUDO_SHELL_ADDITIONAL_COMMAND_OPTIONS=() + + interactive_sudo_shell_required=0 + #if command_type equals "script" + elif [[ "$command_type" == "script" ]]; then + SUDO_SUPPORTED_SHELLS="$SUDO_SUPPORTED_SCRIPT_SHELLS" + SUDO_SUPPORTED_POST_SHELLS="$SUDO_SUPPORTED_INTERACTIVE_SHELLS" + + interactive_sudo_shell_required=0 + else + sudo_log_errors "command_type \"$command_type\" not handled while running sudo command in \"sudo_run\"" + return 1 + fi + + #if command_type equals "script" + if [[ "$command_type" == "script" ]]; then + #if disable_stdin_for_core_script is enabled or stdin is not available while running this script + #force enable disable_stdin_for_core_script + if [[ "$disable_stdin_for_core_script" == "1" ]] || [ ! -t 0 ]; then + disable_stdin_for_core_script=1 + else + disable_stdin_for_core_script=0 + fi + fi + + + #if run_interactive_post_sudo_shell_after_running_core_script is enabled and command_type equals "script" + if [[ "$run_interactive_post_sudo_shell_after_running_core_script" == "1" ]] && [[ "$command_type" == "script" ]]; then + #if same_sudo_post_shell_home_as_sudo_shell_home is enabled + if [[ "$same_sudo_post_shell_home_as_sudo_shell_home" == "1" ]]; then + #override SUDO_POST_SHELL_HOME + SUDO_POST_SHELL_HOME="$SUDO_SHELL_HOME" + fi + else + #unset SUDO_POST_SHELL_HOME so that SUDO_POST_SHELL variables are not set + SUDO_POST_SHELL_HOME="" + fi + + + #test if bash shell executable file exists at BASH_SHELL_PATH + #this is to ensure that bash shell is always the default shell if '--shell` and '--post-shell' options are not passed + #and the shell is automatically validated so that it doesn't have to again in sudo_set_sudo_shell and sudo_set_sudo_post_shell + #bash shell is also needed by the 'su --shell' command + #sudo_run_file_type_tests_on_path label path log_file_tests_failure_errors show_stat_output_on_file_tests_failure check_if_absolute_path file_type_tests + sudo_run_file_type_tests_on_path "BASH_SHELL_PATH" "$BASH_SHELL_PATH" 1 1 1 "frx" || return $? + + #set SU and SU_BIN_PATH + sudo_set_su_variables || return $? + + #set required variables to be used by this script + sudo_set_required_variables || return $? + + #set priority_dependent_variables with termux priority + sudo_set_priority_dependent_variables "termux" || return $? + + #export variables and functions to be used by this script + sudo_export_or_unexport_shared_variables_for_su export || return $? + + #set SU_ENV_COMMAND + sudo_set_su_env_command || return $? + + + + #set SUDO_SHELL and SUDO_SHELL_BASENAME + sudo_set_sudo_shell || return $? + + #if command_type does not equal "path" + #command_type "path" does not use an interactive shell so no need to set these + if [[ "$command_type" != "path" ]]; then + #set SUDO_SHELL_RCFILE and SUDO_SHELL_RCFILE_VALUE to be sourced when SUDO_SHELL is run + sudo_set_sudo_shell_rcfile || return $? + + #set SUDO_SHELL_HISTFILE + sudo_set_sudo_shell_histfile || return $? + fi + + #set SUDO_SHELL_COMMAND and SUDO_SHELL_INTERACTIVE_COMMAND command to be run + sudo_set_sudo_shell_command || return $? + + + + #if SUDO_POST_SHELL_HOME is set + if [ ! -z "$SUDO_POST_SHELL_HOME" ]; then + #if same_sudo_post_shell_as_sudo_shell is enabled + if [[ "$same_sudo_post_shell_as_sudo_shell" == "1" ]]; then + #override SUDO_POST_SHELL + SUDO_POST_SHELL="$SUDO_SHELL" + fi + + #set SUDO_POST_SHELL and SUDO_POST_SHELL_BASENAME + sudo_set_sudo_post_shell || return $? + + #set SUDO_POST_SHELL_RCFILE and SUDO_POST_SHELL_RCFILE_VALUE to be sourced when SUDO_POST_SHELL is run + sudo_set_sudo_post_shell_rcfile || return $? + + #set SUDO_POST_SHELL_HISTFILE file + sudo_set_sudo_post_shell_histfile || return $? + + #set SUDO_POST_SHELL_INTERACTIVE_COMMAND command to be run + sudo_set_sudo_post_shell_command || return $? + fi + + + + #if command_type equals "path" + if [[ "$command_type" == "path" ]]; then + #set SUDO_PATH_COMMAND + sudo_set_sudo_command_path || return $? + fi + + + + #set PRE_SUDO_SHELL_COMMANDS_TO_RUN + sudo_set_pre_sudo_shell_commands_to_run || return $? + + #if SUDO_POST_SHELL_HOME is set + if [ ! -z "$SUDO_POST_SHELL_HOME" ]; then + #set PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN + sudo_set_pre_sudo_post_shell_commands_to_run || return $? + fi + + + + #set traps to run commands before exiting sudo + set_sudo_traps || return $? + + + + #create SUDO_SHELL_HOME, SUDO_SHELL_RCFILE, SUDO_SHELL_HISTFILE and/or + #SUDO_POST_SHELL_HOME, SUDO_POST_SHELL_RCFILE, SUDO_POST_SHELL_HISTFILE + #and SUDO_SHELL_WORKING_DIR if missing + #and SUDO_TEMP_DIRECTORY if required + #android rootfs "/" partition and/or system "/system" partition may be remounted + #as rw if they are to be used for SUDO_SHELL_HOME, SUDO_POST_SHELL_HOME or SUDO_SHELL_WORKING_DIR + sudo_setup_sudo_shell_home_and_working_environment_wrapper + return_value=$? + if [ $return_value -ne 0 ]; then + #if SUDO_TEMP_DIRECTORY_FD is set, then close it + if [ ! -z "$SUDO_TEMP_DIRECTORY_FD" ]; then + exec {SUDO_TEMP_DIRECTORY_FD}>&- + fi + return $return_value + fi + + + + #set SU_RUN_COMMAND + sudo_set_su_run_command || return $? + + #run sudo command depending on command type + + local SUDO_EXIT_CODE=0 + + BASH_SHELL_COMMAND=("$BASH_SHELL_PATH" "--noprofile" "--norc") + printf -v "BASH_SHELL_COMMAND_STRING" "%q " "${BASH_SHELL_COMMAND[@]}" + + #if command_type equals "su" or "asu" + if [[ "$command_type" == "su" ]] || [[ "$command_type" == "asu" ]]; then + + #if SUDO_SHELL_INTERACTIVE_COMMAND is not set + if [ ${#SUDO_SHELL_INTERACTIVE_COMMAND[@]} -eq 0 ]; then + sudo_log_errors "SUDO_SHELL_INTERACTIVE_COMMAND is not set while running sudo $command_type command in \"sudo_run\"" + return 1 + fi + + #unexport variables not to be used by su anymore + sudo_export_or_unexport_shared_variables_for_su unexport || return $? + + + #set SUDO_COMMAND_TO_RUN to run su that will run PRE_SUDO_SHELL_COMMANDS_TO_RUN commands and then + #start an interactive SUDO_SHELL + SUDO_COMMAND_TO_RUN="$SU_RUN_COMMAND $PRE_SUDO_SHELL_COMMANDS_TO_RUN" + printf -v "SUDO_SHELL_INTERACTIVE_COMMAND_STRING" "$SUDO_SHELL_INTERACTIVE_COMMAND_PRINT_FORMAT" "${SUDO_SHELL_INTERACTIVE_COMMAND[@]}" + SUDO_COMMAND_TO_RUN+="$SUDO_SHELL_INTERACTIVE_COMMAND_STRING" + + #if SUDO_SHELL_STDIN_STRING is set + if [ ! -z "$SUDO_SHELL_STDIN_STRING" ]; then + #if SUDO_SHELL_BASENAME is in SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT + if [[ " $SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT " == *" $SUDO_SHELL_BASENAME "* ]]; then + #pass SUDO_SHELL_STDIN_STRING as stdin with a herestring to SUDO_SHELL + SUDO_COMMAND_TO_RUN+=' <<<'"'${SUDO_SHELL_STDIN_STRING//\'/\'\\\'\'}'" + else + #pass SUDO_SHELL_STDIN_STRING as stdin with process substitution to SUDO_SHELL + SUDO_COMMAND_TO_RUN+=' < <(printf "%s" '"'${SUDO_SHELL_STDIN_STRING//\'/\'\\\'\'}'"')' + fi + fi + + #if exec_sudo_shell is enabled, then prepend "exec" + if [[ "$exec_sudo_shell" == "1" ]]; then + SUDO_COMMAND_TO_RUN="exec $SUDO_COMMAND_TO_RUN" + fi + + + sudo_log_literal 2 "\n\n\nRunning sudo $command_type commmand" + sudo_log 1 $'\n'"SUDO_COMMAND_TO_RUN=\`$SUDO_COMMAND_TO_RUN\`" + + #if dry_run_sudo is not enabled + if [[ "$dry_run_sudo" != "1" ]]; then + #run SUDO_COMMAND_TO_RUN + sudo_unset_pre_su_variables + $SUDO_COMMAND_TO_RUN + SUDO_EXIT_CODE=$? + sudo_set_post_su_variables + fi + + + #if command_type equals "path" + elif [[ "$command_type" == "path" ]]; then + + #set script to run in SUDO_PATH_COMMAND_TO_RUN + sudo_set_sudo_path_command || return $? + + #unexport variables not to be used by su anymore + sudo_export_or_unexport_shared_variables_for_su unexport || return $? + + + #set SUDO_COMMAND_TO_RUN to run su that will run PRE_SUDO_SHELL_COMMANDS_TO_RUN commands and then + #run the SUDO_PATH_COMMAND_TO_RUN + SUDO_COMMAND_TO_RUN="$SU_RUN_COMMAND $PRE_SUDO_SHELL_COMMANDS_TO_RUN" + + #if exec_sudo_shell is enabled, then prepend "exec" + if [[ "$exec_sudo_shell" == "1" ]]; then + SUDO_COMMAND_TO_RUN="exec $SUDO_COMMAND_TO_RUN" + fi + + #pass the SUDO_PATH_FD_PATH to the BASH_SHELL as an argument + SUDO_COMMAND_TO_RUN+="$BASH_SHELL_COMMAND_STRING \"$SUDO_PATH_FD_PATH\"" + + + sudo_log_literal 2 "\n\n\nRunning sudo $command_type commmand" + sudo_log 1 $'\n'"SUDO_COMMAND_TO_RUN=\`$SUDO_COMMAND_TO_RUN\`" + + #if disable_arguments_logging is not enabled + if [[ "$disable_arguments_logging" != "1" ]]; then + sudo_log 1 $'\n'"SUDO_PATH_COMMAND_TO_RUN=\`$SUDO_PATH_COMMAND_TO_RUN\`" + fi + + #if dry_run_sudo is not enabled + if [[ "$dry_run_sudo" != "1" ]]; then + #run SUDO_COMMAND_TO_RUN + sudo_unset_pre_su_variables + $SUDO_COMMAND_TO_RUN + SUDO_EXIT_CODE=$? + sudo_set_post_su_variables + fi + + #if SUDO_PATH_FD_PATH is set, then close it + if [ ! -z "$SUDO_PATH_FD_PATH" ]; then + exec {SUDO_PATH_FD_PATH}>&- + SUDO_PATH_FD_PATH="" + fi + + + #if command_type equals "script" + elif [[ "$command_type" == "script" ]]; then + + #if SUDO_SHELL_COMMAND is not set + if [ ${#SUDO_SHELL_COMMAND[@]} -eq 0 ]; then + sudo_log_errors "SUDO_SHELL_COMMAND is not set while running sudo $command_type command in \"sudo_run\"" + return 1 + fi + + #if run_interactive_post_sudo_shell_after_running_core_script is enabled and SUDO_POST_SHELL_INTERACTIVE_COMMAND is not set + if [[ "$run_interactive_post_sudo_shell_after_running_core_script" == "1" ]] && [ ${#SUDO_POST_SHELL_INTERACTIVE_COMMAND[@]} -eq 0 ]; then + sudo_log_errors "SUDO_POST_SHELL_INTERACTIVE_COMMAND is not set while running sudo $command_type command in \"sudo_run\"" + return 1 + fi + + #set script to run in SUDO_SCRIPT_COMMAND_TO_RUN + sudo_set_sudo_script_command || return $? + + #unexport variables not to be used by su anymore + sudo_export_or_unexport_shared_variables_for_su unexport || return $? + + + #set SUDO_COMMAND_TO_RUN to run su that will run the SUDO_SCRIPT_COMMAND_TO_RUN + SUDO_COMMAND_TO_RUN="$SU_RUN_COMMAND " + + #if exec_sudo_shell is enabled, then prepend "exec" + if [[ "$exec_sudo_shell" == "1" ]]; then + SUDO_COMMAND_TO_RUN="exec $SUDO_COMMAND_TO_RUN" + fi + + #pass the SUDO_SCRIPT_FD_PATH to the BASH_SHELL as an argument + SUDO_COMMAND_TO_RUN+="$BASH_SHELL_COMMAND_STRING \"$SUDO_SCRIPT_FD_PATH\"" + + + sudo_log_literal 2 "\n\n\nRunning sudo $command_type commmand" + sudo_log 1 $'\n'"SUDO_COMMAND_TO_RUN=\`$SUDO_COMMAND_TO_RUN\`" + + #if disable_arguments_logging is not enabled + if [[ "$disable_arguments_logging" != "1" ]]; then + sudo_log 1 $'\n'"SUDO_SCRIPT_COMMAND_TO_RUN=\`$SUDO_SCRIPT_COMMAND_TO_RUN\`" + fi + + #if dry_run_sudo is not enabled + if [[ "$dry_run_sudo" != "1" ]]; then + #run SUDO_COMMAND_TO_RUN + sudo_unset_pre_su_variables + $SUDO_COMMAND_TO_RUN + SUDO_EXIT_CODE=$? + sudo_set_post_su_variables + fi + + #if SUDO_SCRIPT_FD is set, then close it + if [ ! -z "$SUDO_SCRIPT_FD" ]; then + exec {SUDO_SCRIPT_FD}>&- + SUDO_SCRIPT_FD="" + fi + + + else + sudo_log_errors "command_type \"$command_type\" not handled while running sudo command in \"sudo_run\"" + return 1 + fi + + #if dry_run_sudo is not enabled + if [[ "$dry_run_sudo" != "1" ]]; then + sudo_log 2 $'\n'"SUDO_EXIT_CODE=\"$SUDO_EXIT_CODE\"" + fi + + #if sudo is being run in an interactive shell, then reset terminal + if [[ $- == *i* ]]; then + stty sane &>/dev/null + fi + + return $SUDO_EXIT_CODE + +} + +#sudo_run_pre_exit_commands exit_code +sudo_run_pre_exit_commands() { + + local return_value + + local exit_code="$1" + + #if hold_after_sudo is enabled + if [[ "$hold_after_sudo" == "1" ]]; then + #if hold_only_on_failure is not enabled or (hold_only_on_failure is enabled and exit_code does not equal 0) + if [[ "$hold_only_on_failure" != "1" ]] || ([[ "$hold_only_on_failure" == "1" ]] && [[ "$exit_code" != "0" ]]); then + #if stdin and stdout are available + if [ -t 0 ] && [ -t 1 ]; then + #if HOLD_STRING_AFTER_SUDO is set + if [ ! -z "$HOLD_STRING_AFTER_SUDO" ]; then + local valid_alphanumeric_and_punct_string_regex='^[[:alnum:][:punct:]]+$' + + if [[ "$HOLD_STRING_AFTER_SUDO" =~ $valid_alphanumeric_and_punct_string_regex ]]; then + #read from stdin until HOLD_STRING_AFTER_SUDO is correctly entered + #read should never timeout because of TMOUT=0 + local exit_while=0 + while [[ $exit_while != "1" ]]; do + TMOUT=0 read -p "Enter \"$HOLD_STRING_AFTER_SUDO\" to exit:"$'\n' input + if [ $? -ne 0 ] || [[ "$input" == "$HOLD_STRING_AFTER_SUDO" ]]; then + exit_while=1 + fi + done + else + sudo_log_errors "HOLD_STRING_AFTER_SUDO \"$HOLD_STRING_AFTER_SUDO\" is invalid. It can only contain alphanumeric and punctuation characters" + return 1 + fi + + #else if only '--hold' was passed + else + #read any character from stdin in silent mode + #read should never timeout because of TMOUT=0 + TMOUT=0 read -s -n 1 + fi + fi + fi + fi + + #if sleep_after_sudo is enabled + if [[ "$sleep_after_sudo" == "1" ]]; then + #if sleep_only_on_failure is not enabled or (sleep_only_on_failure is enabled and exit_code does not equal 0) + if [[ "$sleep_only_on_failure" != "1" ]] || ([[ "$sleep_only_on_failure" == "1" ]] && [[ "$exit_code" != "0" ]]); then + local valid_floating_point_number_regex='^[0-9]+(\.[0-9]+)?$' + + if [[ "$SLEEP_TIME_AFTER_SUDO" =~ $valid_floating_point_number_regex ]]; then + #sleep for SLEEP_TIME_AFTER_SUDO seconds + sleep "$SLEEP_TIME_AFTER_SUDO" + else + sudo_log_errors "SLEEP_TIME_AFTER_SUDO \"$SLEEP_TIME_AFTER_SUDO\" is not a valid floating point number" + return 1 + fi + fi + fi + + return 0 + +} + +sudo_set_su_variables() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_su_variables" + + SU="" + SU_BIN_PATH="" + + local -a su_search_paths=("/su/bin/su") + + #if "/sbin" is accessible + if [ -x "/sbin" ]; then + su_search_paths+=("/sbin/su" "/sbin/bin/su") + fi + + su_search_paths+=("/system/xbin/su" "/system/bin/su" "/su/xbin/su" "/magisk/.core/bin/su") + + local su + local su_found=0 + local su_help="" + + for su in "${su_search_paths[@]}"; do + if [ -x "$su" ]; then + sudo_log 2 "A su binary found at \"$su\"" + su_found=1 + + sudo_unset_pre_su_variables + su_help="$($su --help < /dev/null)" + sudo_set_post_su_variables + + #if '--shell' option is not supported, then continue searching + if [[ "$su_help" != *"--shell"* ]]; then + sudo_log 2 "The su binary found does not support '--shell' option, continuing search" + continue + #if '--preserve-environment' option is not supported, then continue searching + elif [[ "$su_help" != *"--preserve-environment"* ]]; then + sudo_log 2 "The su binary found does not support '--preserve-environment' option, continuing search" + continue + #if '--mount-master' option is not supported, then continue searching + elif [[ "$su_help" != *"--mount-master"* ]]; then + sudo_log 2 "The su binary found does not support '--mount-master' option, continuing search" + continue + #else use the su + else + SU="$su" + SU_BIN_PATH="${su%/su}" + break + fi + + sudo_log 2 "" + fi + done + + #if SU is not set + if [ -z "$SU" ]; then + #if su_found is not set to "1" + if [[ "$su_found" != "1" ]]; then + sudo_log_errors "No 'su' binary found" + else + sudo_log_errors "No 'su' binary found that supports the '--shell', '--preserve-environment' and '--mount-master' options" + fi + + sudo_log_errors "sudo requires a 'su' binary that supports the '--shell', '--preserve-environment' and '--mount-master' options" + return 1 + fi + + sudo_log 2 "SU=\"$SU\"" + sudo_log 2 "SU_BIN_PATH=\"$SU_BIN_PATH\"" + + return 0 + +} + +#set the su command that will be used to set up the sudo environment +sudo_set_su_env_command() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_su_env_command" + + #set SU_ENV_COMMAND_VARIABLES_TO_EXPORT + sudo_set_su_env_command_variables_to_export || return $? + + SU_ENV_COMMAND="$SU" + + #if rootfs_partition_dependent_paths or system_partition_dependent_paths is set + if [ ! -z "$rootfs_partition_dependent_paths" ] || [ ! -z "$system_partition_dependent_paths" ]; then + #if SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS does not start with the '--mount-master' or '-mm' command option, then + #add the '--mount-master' option to SU_ENV_COMMAND at the start as required by su + if [[ ! "${SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS[*]}" =~ $valid_su_mount_master_option_prefixed_string ]]; then + SU_ENV_COMMAND+=" --mount-master" + fi + fi + + #if SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS is set, then add the options to SU_ENV_COMMAND + if [ ${#SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS[@]} -ne 0 ]; then + printf -v "SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS_STRING" "%q " "${SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS[@]}" + SU_ENV_COMMAND+=" $SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS_STRING" + SU_ENV_COMMAND="${SU_ENV_COMMAND% }" #remove trailing space + fi + + #We rely on termux to set up most of the environment since it will be dependent on android os and device + #hence '--preserve-environment' is required + #we also source the sudo script in some SU_ENV_COMMAND commands to define its functions + #so that only the parent function is called within a new su shell instead of + #calling a new su shell for every root command that needs to be run inside + #the child functions + #this has drastic performance improvements in some cases where multiple root + #commands need to be run since creating a new su shell for each command has too + #much overhead + SU_ENV_COMMAND+=" --preserve-environment --shell $BASH_SHELL_PATH -c $SU_ENV_COMMAND_VARIABLES_TO_EXPORT " + + sudo_log 2 "SU_ENV_COMMAND=\`$SU_ENV_COMMAND\`" + + return 0 + +} + +#set the su command that will be used to run the sudo command_type command +sudo_set_su_run_command() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_su_run_command" + + SU_RUN_COMMAND="$SU" + + #if rootfs_partition_dependent_paths or system_partition_dependent_paths is set + if [ ! -z "$rootfs_partition_dependent_paths" ] || [ ! -z "$system_partition_dependent_paths" ]; then + #if SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS does not start with the '--mount-master' or '-mm' command option, then + #add the '--mount-master' option to SU_RUN_COMMAND at the start as required by su + if [[ ! "${SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS[*]}" =~ $valid_su_mount_master_option_prefixed_string ]]; then + SU_RUN_COMMAND+=" --mount-master" + fi + fi + + #if SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS is set, then add the options to SU_RUN_COMMAND + if [ ${#SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS[@]} -ne 0 ]; then + printf -v "SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS_STRING" "%q " "${SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS[@]}" + SU_RUN_COMMAND+=" $SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS_STRING" + SU_RUN_COMMAND="${SU_RUN_COMMAND% }" #remove trailing space + fi + + #if disable_preserve_environment_for_su is not enabled, then use the option + if [[ "$disable_preserve_environment_for_su" != "1" ]]; then + SU_RUN_COMMAND+=" --preserve-environment" + fi + + SU_RUN_COMMAND+=" --shell $BASH_SHELL_PATH -c" + + sudo_log 2 "SU_RUN_COMMAND=\`$SU_RUN_COMMAND\`" + + return 0 + +} + +sudo_set_required_variables() { + + local return_value + + #do not modify unless you known what you are doing + + sudo_log_literal 2 "\nRunning sudo_set_required_variables" + + #set LD_PRELOAD + export LD_PRELOAD="$TERMUX_PREFIX/lib/libtermux-exec.so" + + SUDO_LD_PRELOAD="$LD_PRELOAD" + + local remove_duplicates_in_path_variable=0 + local remove_duplicates_in_ld_library_path_variable=0 + + #store PATH and LD_LIBRARY_PATH + OLD_PATH="$PATH" + OLD_LD_LIBRARY_PATH="$LD_LIBRARY_PATH" + + + #set PATH + PATH="$TERMUX_PATH:$SYS_XBIN:$SYS_BIN" + + sudo_log 2 "PATH=\"$PATH\"" + + #find machine arch + ARCH="$(uname -m)" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to find machine arch" + return $return_value + fi + + sudo_log 2 "ARCH=\"$ARCH\"" + + #set LD_LIBRARY_PATH + #if ARCH is 64 bit + if [[ "$ARCH" == *64 ]]; then + LD_LIBRARY_PATH="$TERMUX_LD_LIBRARY_PATH:/system/lib64:/system/lib" + else + LD_LIBRARY_PATH="$TERMUX_LD_LIBRARY_PATH:/system/lib" + fi + + SUDO_LD_LIBRARY_PATH="$LD_LIBRARY_PATH" + + sudo_log 2 "LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH\"" + + #set ANDROID_PATH + + #if SU_BIN_PATH is not empty, then append it to ANDROID_PATH + if [ ! -z "$SU_BIN_PATH" ]; then + ANDROID_PATH+=":$SU_BIN_PATH" + fi + + #if ANDROID_OTHER_BIN_PATHS is not empty, then append it to ANDROID_PATH + if [ ! -z "$ANDROID_OTHER_BIN_PATHS" ]; then + ANDROID_PATH+=":$ANDROID_OTHER_BIN_PATHS" + fi + + #set TERMUX_PRIORITY_PATH with priority to termux binary and executable paths + TERMUX_PRIORITY_PATH="$TERMUX_PATH:$ANDROID_PATH" + + #set ANDROID_PRIORITY_PATH with priority to android binary and executable paths + ANDROID_PRIORITY_PATH="$ANDROID_PATH:$TERMUX_PATH" + + #if export_all_existing_paths_in_path_variable is enabled and OLD_PATH is not empty, then append it to PATH variables + if [[ "$export_all_existing_paths_in_path_variable" == "1" ]] && [ ! -z "$OLD_PATH" ]; then + TERMUX_PRIORITY_PATH+=":$OLD_PATH" + ANDROID_PRIORITY_PATH+=":$OLD_PATH" + remove_duplicates_in_path_variable=1 + fi + + #if ADDITIONAL_PATHS_TO_EXPORT is not empty, then append it to PATH variables + if [ ! -z "$ADDITIONAL_PATHS_TO_EXPORT" ]; then + TERMUX_PRIORITY_PATH+=":$ADDITIONAL_PATHS_TO_EXPORT" + ANDROID_PRIORITY_PATH+=":$ADDITIONAL_PATHS_TO_EXPORT" + remove_duplicates_in_path_variable=1 + fi + + #remove_duplicates_in_path_variable is only enabled if custom paths are added to the PATH variables + #this will reduce execution time since an external call to awk with a subshell will not be made + #default paths set must not have any duplicates + + #remove duplicates from TERMUX_PRIORITY_PATH and check if it is valid and can be used as the PATH variable + sudo_parse_and_validate_path_variable "TERMUX_PRIORITY_PATH" "TERMUX_PRIORITY_PATH" "$TERMUX_PRIORITY_PATH" "$remove_duplicates_in_path_variable" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to parse and validate \"TERMUX_PRIORITY_PATH\"" + return $return_value + fi + + #remove duplicates from ANDROID_PRIORITY_PATH and check if it is valid and can be used as the PATH variable + sudo_parse_and_validate_path_variable "ANDROID_PRIORITY_PATH" "ANDROID_PRIORITY_PATH" "$ANDROID_PRIORITY_PATH" "$remove_duplicates_in_path_variable" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to parse and validate \"ANDROID_PRIORITY_PATH\"" + return $return_value + fi + + + #set LD_LIBRARY_PATH_TO_EXPORT + #if ARCH is 64 bit + if [[ "$ARCH" == *64 ]]; then + ANDROID_LD_LIBRARY_PATH="/system/lib64:/system/lib" + #if ARCH is 32 bit + else + ANDROID_LD_LIBRARY_PATH="/system/lib" + fi + + #set TERMUX_PRIORITY_LD_LIBRARY_PATH with priority to termux library paths + TERMUX_PRIORITY_LD_LIBRARY_PATH="$TERMUX_LD_LIBRARY_PATH:$ANDROID_LD_LIBRARY_PATH" + + #set ANDROID_PRIORITY_LD_LIBRARY_PATH with priority to android library paths + ANDROID_PRIORITY_LD_LIBRARY_PATH="$ANDROID_LD_LIBRARY_PATH:$TERMUX_LD_LIBRARY_PATH" + + #if export_all_existing_paths_in_ld_library_path_variable is enabled and OLD_LD_LIBRARY_PATH is not empty, then append it to LD_LIBRARY_PATH variables + if [[ "$export_all_existing_paths_in_ld_library_path_variable" == "1" ]] && [ ! -z "$OLD_LD_LIBRARY_PATH" ]; then + TERMUX_PRIORITY_LD_LIBRARY_PATH+=":$OLD_LD_LIBRARY_PATH" + ANDROID_PRIORITY_LD_LIBRARY_PATH+=":$OLD_LD_LIBRARY_PATH" + remove_duplicates_in_ld_library_path_variable=1 + fi + + #if ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT is not empty, then append it to LD_LIBRARY_PATH variables + if [ ! -z "$ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT" ]; then + TERMUX_PRIORITY_LD_LIBRARY_PATH+=":$ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT" + ANDROID_PRIORITY_LD_LIBRARY_PATH+=":$ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT" + remove_duplicates_in_ld_library_path_variable=1 + fi + + #remove_duplicates_in_path_variable is only enabled if custom paths are added to the LD_LIBRARY_PATH variables + #this will reduce execution time since an external call to awk with a subshell will not be made + #default paths set must not have any duplicates + + #remove duplicates from TERMUX_PRIORITY_LD_LIBRARY_PATH and check if it is valid and can be used as the LD_LIBRARY_PATH variable + sudo_parse_and_validate_path_variable "TERMUX_PRIORITY_LD_LIBRARY_PATH" "TERMUX_PRIORITY_LD_LIBRARY_PATH" "$TERMUX_PRIORITY_LD_LIBRARY_PATH" "$remove_duplicates_in_ld_library_path_variable" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to parse and validate \"TERMUX_PRIORITY_LD_LIBRARY_PATH\"" + return $return_value + fi + + #remove duplicates from ANDROID_PRIORITY_LD_LIBRARY_PATH and check if it is valid and can be used as the LD_LIBRARY_PATH variable + sudo_parse_and_validate_path_variable "ANDROID_PRIORITY_LD_LIBRARY_PATH" "ANDROID_PRIORITY_LD_LIBRARY_PATH" "$ANDROID_PRIORITY_LD_LIBRARY_PATH" "$remove_duplicates_in_ld_library_path_variable" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to parse and validate \"ANDROID_PRIORITY_LD_LIBRARY_PATH\"" + return $return_value + fi + + + #set TERMUX_PRIORITY_LD_PRELOAD_COMMAND to set LD_PRELOAD to libtermux-exec.so + TERMUX_PRIORITY_LD_PRELOAD_COMMAND="export LD_PRELOAD='$TERMUX_PREFIX/lib/libtermux-exec.so';" + + #set ANDROID_PRIORITY_LD_PRELOAD_COMMAND to unset LD_PRELOAD + ANDROID_PRIORITY_LD_PRELOAD_COMMAND="unset LD_PRELOAD;" + + + + #set SUDO_SHELL_HOME + sudo_trim_trailing_newlines "SUDO_SHELL_HOME" "$SUDO_SHELL_HOME" + + #replace "$PREFIX/" or "~/" prefix with termux absolute paths in SUDO_SHELL_HOME + #canonicalize_path is only enabled if a custom path was passed for SUDO_SHELL_HOME by the user + #this will reduce execution time since an external call to readlink with a subshell will not be made + sudo_expand_termux_path "SUDO_SHELL_HOME" "SUDO_SHELL_HOME" "$SUDO_SHELL_HOME" "$TERMUX_PREFIX" "$TERMUX_HOME" "$custom_sudo_shell_home_set" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to expand SUDO_SHELL_HOME \"$SUDO_SHELL_HOME\"" + return $return_value + fi + + #if SUDO_SHELL_HOME is not a valid absolute path + if [[ ! "$SUDO_SHELL_HOME" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_SHELL_HOME \"$SUDO_SHELL_HOME\" is not a valid absolute path" + return 1 + fi + + sudo_log 2 "SUDO_SHELL_HOME=\"$SUDO_SHELL_HOME\"" + + #find the parent directory of the SUDO_SHELL_HOME + SUDO_SHELL_HOME_BASENAME="${SUDO_SHELL_HOME##*/}" #strip longest match of */ from start + SUDO_SHELL_HOME_PARENT_DIR="${SUDO_SHELL_HOME:0:${#SUDO_SHELL_HOME} - ${#SUDO_SHELL_HOME_BASENAME}}" #substring from 0 to position of basename + case $SUDO_SHELL_HOME_PARENT_DIR in *[!/]*/) SUDO_SHELL_HOME_PARENT_DIR=${SUDO_SHELL_HOME_PARENT_DIR%"${SUDO_SHELL_HOME_PARENT_DIR##*[!/]}"};; *[/]) SUDO_SHELL_HOME_PARENT_DIR="/";; esac #remove trailing slashes if not root + + sudo_log 2 "SUDO_SHELL_HOME_PARENT_DIR=\"$SUDO_SHELL_HOME_PARENT_DIR\"" + + + #use SUDO_SHELL_HOME as SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY + SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY="$SUDO_SHELL_HOME" + + + + #if SUDO_POST_SHELL_HOME is set + if [ ! -z "$SUDO_POST_SHELL_HOME" ]; then + #set SUDO_POST_SHELL_HOME + sudo_trim_trailing_newlines "SUDO_POST_SHELL_HOME" "$SUDO_POST_SHELL_HOME" + + #replace "$PREFIX/" or "~/" prefix with termux absolute paths in SUDO_POST_SHELL_HOME + #canonicalize_path is only enabled if a custom path was passed for SUDO_POST_SHELL_HOME by the user + #this will reduce execution time since an external call to readlink with a subshell will not be made + sudo_expand_termux_path "SUDO_POST_SHELL_HOME" "SUDO_POST_SHELL_HOME" "$SUDO_POST_SHELL_HOME" "$TERMUX_PREFIX" "$TERMUX_HOME" "$custom_sudo_post_shell_home_set" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to expand \"SUDO_POST_SHELL_HOME\"" + return $return_value + fi + + #if SUDO_POST_SHELL_HOME is not a valid absolute path + if [[ ! "$SUDO_POST_SHELL_HOME" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_POST_SHELL_HOME \"$SUDO_POST_SHELL_HOME\" is not a valid absolute path" + return 1 + fi + + sudo_log 2 "SUDO_POST_SHELL_HOME=\"$SUDO_POST_SHELL_HOME\"" + + #find the parent directory of the SUDO_POST_SHELL_HOME + SUDO_POST_SHELL_HOME_BASENAME="${SUDO_POST_SHELL_HOME##*/}" #strip longest match of */ from start + SUDO_POST_SHELL_HOME_PARENT_DIR="${SUDO_POST_SHELL_HOME:0:${#SUDO_POST_SHELL_HOME} - ${#SUDO_POST_SHELL_HOME_BASENAME}}" #substring from 0 to position of basename + case $SUDO_POST_SHELL_HOME_PARENT_DIR in *[!/]*/) SUDO_POST_SHELL_HOME_PARENT_DIR=${SUDO_POST_SHELL_HOME_PARENT_DIR%"${SUDO_POST_SHELL_HOME_PARENT_DIR##*[!/]}"};; *[/]) SUDO_POST_SHELL_HOME_PARENT_DIR="/";; esac #remove trailing slashes if not root + + sudo_log 2 "SUDO_POST_SHELL_HOME_PARENT_DIR=\"$SUDO_POST_SHELL_HOME_PARENT_DIR\"" + fi + + + + #if SUDO_SHELL_WORKING_DIR is set + if [ ! -z "$SUDO_SHELL_WORKING_DIR" ]; then + #set SUDO_SHELL_WORKING_DIR + sudo_set_sudo_shell_working_dir || return $? + fi + + + + #create TMPDIR if missing + sudo_setup_termux_tmp_dir || return $? + + + + #set rootfs_partition_dependent_paths and system_partition_dependent_paths + #which are required by sudo_set_su_env_command and sudo_set_su_run_command functions to add '--mount-master option' + #to SU_ENV_COMMAND and SU_RUN_COMMAND if required and then by the sudo_remount_partitions_for_sudo_shell_homes function + #using parent directories for rootfs checks since some directories in root like /mnt may + #require rootfs to be mounted as rw to be writeable + + rootfs_partition_dependent_paths="" + system_partition_dependent_paths="" + + #if SUDO_SHELL_HOME is in system "/system" partition + if [[ "$SUDO_SHELL_HOME" =~ $valid_path_in_system_partition_regex ]]; then + system_partition_dependent_paths+=" SUDO_SHELL_HOME \"$SUDO_SHELL_HOME\"" + #if SUDO_SHELL_HOME_PARENT_DIR is in rootfs "/" partition + elif [[ "$SUDO_SHELL_HOME_PARENT_DIR" =~ $valid_path_in_rootfs_partition_regex ]]; then + rootfs_partition_dependent_paths+=" SUDO_SHELL_HOME \"$SUDO_SHELL_HOME\"" + fi + + #if SUDO_POST_SHELL_HOME is in system "/system" partition + if [[ "$SUDO_POST_SHELL_HOME" =~ $valid_path_in_system_partition_regex ]]; then + system_partition_dependent_paths+=" SUDO_POST_SHELL_HOME \"$SUDO_POST_SHELL_HOME\"" + #if SUDO_POST_SHELL_HOME_PARENT_DIR is in rootfs "/" partition + elif [[ "$SUDO_POST_SHELL_HOME_PARENT_DIR" =~ $valid_path_in_rootfs_partition_regex ]]; then + rootfs_partition_dependent_paths+=" SUDO_POST_SHELL_HOME \"$SUDO_POST_SHELL_HOME\"" + fi + + #if SUDO_SHELL_WORKING_DIR is in system "/system" partition + if [[ "$SUDO_SHELL_WORKING_DIR" =~ $valid_path_in_system_partition_regex ]]; then + system_partition_dependent_paths+=" SUDO_SHELL_WORKING_DIR \"$SUDO_SHELL_WORKING_DIR\"" + #if SUDO_SHELL_WORKING_DIR_PARENT_DIR is in rootfs "/" partition + elif [[ "$SUDO_SHELL_WORKING_DIR_PARENT_DIR" =~ $valid_path_in_rootfs_partition_regex ]]; then + rootfs_partition_dependent_paths+=" SUDO_SHELL_WORKING_DIR \"$SUDO_SHELL_WORKING_DIR\"" + fi + + #if rootfs_partition_dependent_paths or system_partition_dependent_paths is set + if [ ! -z "$rootfs_partition_dependent_paths" ] || [ ! -z "$system_partition_dependent_paths" ]; then + #find android sdk/os version + ANDROID_SDK_VERSION="$(getprop "ro.build.version.sdk")" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$ANDROID_SDK_VERSION" =~ $valid_number_regex ]]; then + sudo_log_errors "Failure while finding \"ro.build.version.sdk\" property" + sudo_log_errors "ANDROID_SDK_VERSION = \"$ANDROID_SDK_VERSION\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + sudo_log 2 "ANDROID_SDK_VERSION=\"$ANDROID_SDK_VERSION\"" + + # If android >= 10 + if [ "$ANDROID_SDK_VERSION" -ge 29 ]; then + [ ! -z "$rootfs_partition_dependent_paths" ] && sudo_log_errors "The rootfs \"/\" partition cannot be mounted as rw to be used for$rootfs_partition_dependent_paths since in android >= 10 the rootfs partition is likely a read-only system-as-root SAR partition" + [ ! -z "$system_partition_dependent_paths" ] && sudo_log_errors "The system \"/system\" partition cannot be mounted as rw to be used for$system_partition_dependent_paths since in android >= 10 the system partition is an ext4 dedup filesystem" + return 1 + fi + fi + + return 0 + +} + +#function that should be called before running su commands +#this will unset variable so that some su binaries like the ones provided by magisk run successfully +sudo_unset_pre_su_variables() { + unset LD_PRELOAD + unset LD_LIBRARY_PATH +} + +#function that should be called after running su commands +#this will reset variable previously unset by sudo_unset_pre_su_variables so that sudo script commands run successfully +sudo_set_post_su_variables() { + LD_PRELOAD="$SUDO_LD_PRELOAD" + LD_LIBRARY_PATH="$SUDO_LD_LIBRARY_PATH" +} + +#set variables to export with priority to termux or android bin and library paths +#sudo_set_priority_dependent_variables termux|android +sudo_set_priority_dependent_variables() { + + local return_value + + #if parameter count is not 1 + if [ $# -ne 1 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_set_priority_dependent_variables\"" + return 1 + fi + + local priority="$1" + + if [[ "$priority" != "termux" ]] && [[ "$priority" != "android" ]]; then + sudo_log_errors "priority \"$priority\" passed to \"sudo_set_priority_dependent_variables\" does not equal \"termux\" or \"android\"" + return 1 + fi + + #if priority to the previous call to this function was the same, then just return + if [[ "$priority" == "$previous_priority_dependent_variables_priortiy" ]]; then + return 0 + fi + + sudo_log_literal 2 "\nRunning sudo_set_priority_dependent_variables with priority \"$priority\"" + + #set PATH_TO_EXPORT + #if priority equals "termux" then PATH_TO_EXPORT should be set to TERMUX_PRIORITY_PATH + if [[ "$priority" == "termux" ]]; then + PATH_TO_EXPORT="$TERMUX_PRIORITY_PATH" + #if priority equals "android" then PATH_TO_EXPORT should be set to ANDROID_PRIORITY_PATH + else + PATH_TO_EXPORT="$ANDROID_PRIORITY_PATH" + fi + + sudo_log 2 "PATH_TO_EXPORT=\"$PATH_TO_EXPORT\"" + + + #if priority equals "termux" then LD_LIBRARY_PATH_TO_EXPORT should be set to TERMUX_PRIORITY_LD_LIBRARY_PATH + if [[ "$priority" == "termux" ]]; then + LD_LIBRARY_PATH_TO_EXPORT="$TERMUX_PRIORITY_LD_LIBRARY_PATH" + #if priority equals "android" then LD_LIBRARY_PATH_TO_EXPORT should be set to ANDROID_PRIORITY_LD_LIBRARY_PATH + else + LD_LIBRARY_PATH_TO_EXPORT="$ANDROID_PRIORITY_LD_LIBRARY_PATH" + fi + + sudo_log 2 "LD_LIBRARY_PATH_TO_EXPORT=\"$LD_LIBRARY_PATH_TO_EXPORT\"" + + + #if priority equals "termux" then LD_PRELOAD_COMMAND should be set to TERMUX_PRIORITY_LD_PRELOAD_COMMAND + if [[ "$priority" == "termux" ]]; then + LD_PRELOAD_COMMAND="$TERMUX_PRIORITY_LD_PRELOAD_COMMAND" + #if priority equals "android" then LD_PRELOAD_COMMAND should be set to ANDROID_PRIORITY_LD_PRELOAD_COMMAND + else + LD_PRELOAD_COMMAND="$ANDROID_PRIORITY_LD_PRELOAD_COMMAND" + fi + + sudo_log 2 "LD_PRELOAD_COMMAND=\"$LD_PRELOAD_COMMAND\"" + + + previous_priority_dependent_variables_priortiy="$priority" + + return 0 + +} + +#the function is used to export variables so that they can be used from within the SU_ENV_COMMAND su bash shell +#the variables must be unexported after the sudo environment has been setup up +#and only the command_type SU_RUN_COMMAND su needs to be run +#the command_type su shell should not have the variables of the sudo script in its environment +#sudo_export_or_unexport_shared_variables_for_su export|unexport +sudo_export_or_unexport_shared_variables_for_su() { + + local return_value + + #if parameter count is not 1 + if [ $# -ne 1 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_export_or_unexport_shared_variables_for_su\"" + return 1 + fi + + local command="$1" + + if [[ "$command" != "export" ]] && [[ "$command" != "unexport" ]]; then + sudo_log_errors "command \"$command\" passed to \"sudo_export_or_unexport_shared_variables_for_su\" does not equal \"export\" or \"unexport\"" + return 1 + fi + + #if command equals "unexport" + if [[ "$command" == "unexport" ]]; then + command="export -n" + fi + + $command sudo_verbose_level + + if [ ! -z "$sudo_start_time" ]; then + $command sudo_start_time + fi + + return 0 + +} + +sudo_set_su_env_command_variables_to_export() { + + local return_value + + #set SU_ENV_COMMAND_VARIABLES_TO_EXPORT + SU_ENV_COMMAND_VARIABLES_TO_EXPORT="export PATH='${PATH_TO_EXPORT//\'/\'\\\'\'}'; export LD_LIBRARY_PATH='${LD_LIBRARY_PATH_TO_EXPORT//\'/\'\\\'\'}'; export HOME='${SUDO_SHELL_HOME//\'/\'\\\'\'}'; export LANG='${LANG//\'/\'\\\'\'}'; $LD_PRELOAD_COMMAND" + + return 0 + +} + +sudo_set_pre_sudo_shell_commands_to_run() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_pre_sudo_shell_commands_to_run" + + local priority + + #if command_type equals "asu" + if [[ "$command_type" == "asu" ]]; then + #set PRE_SUDO_SHELL_COMMANDS_TO_RUN with priority to android bin and library paths + sudo_log 2 "Setting PRE_SUDO_SHELL_COMMANDS_TO_RUN with priority to android bin and library paths since command_type is \"asu\"" + priority="android" + #if command_type equals "path" and (SUDO_PATH_COMMAND is in /system partition or force_set_priority_to_android_paths is enabled) + elif [[ "$command_type" == "path" ]] && ([[ "$SUDO_PATH_COMMAND" =~ $valid_path_in_system_partition_regex ]] || [[ "$force_set_priority_to_android_paths" == "1" ]]); then + #set PRE_SUDO_SHELL_COMMANDS_TO_RUN with priority to android bin and library paths + sudo_log 2 "Setting PRE_SUDO_SHELL_COMMANDS_TO_RUN with priority to android bin and library paths since command_type is \"path\" and SUDO_PATH_COMMAND is in \"/system\" partition or force_set_priority_to_android_paths is enabled" + priority="android" + else + #set PRE_SUDO_SHELL_COMMANDS_TO_RUN with priority to termux bin and library paths + sudo_log 2 "Setting PRE_SUDO_SHELL_COMMANDS_TO_RUN with priority to termux bin and library paths" + priority="termux" + fi + + #set priority_dependent_variables with new priority if needed + sudo_set_priority_dependent_variables "$priority" || return $? + + #set PRE_SUDO_SHELL_COMMANDS_TO_RUN + #set variables that need to be exported when running the sudo shell + PRE_SUDO_SHELL_COMMANDS_TO_RUN="" + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export TERMUX_PRIORITY_PATH='${TERMUX_PRIORITY_PATH//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export TERMUX_PRIORITY_LD_LIBRARY_PATH='${TERMUX_PRIORITY_LD_LIBRARY_PATH//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export ANDROID_PRIORITY_PATH='${ANDROID_PRIORITY_PATH//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export ANDROID_PRIORITY_LD_LIBRARY_PATH='${ANDROID_PRIORITY_LD_LIBRARY_PATH//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export PATH='${PATH_TO_EXPORT//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export LD_LIBRARY_PATH='${LD_LIBRARY_PATH_TO_EXPORT//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export PREFIX='$TERMUX_PREFIX';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export TMPDIR='$TMPDIR';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export HOME='${SUDO_SHELL_HOME//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export TERM='${TERM//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export LANG='${LANG//\'/\'\\\'\'}';"$'\n' + + #if command_type equals "script" and disable_stdin_for_core_script is enabled + if [[ "$command_type" == "script" ]] && [[ "$disable_stdin_for_core_script" == "1" ]]; then + #unset PS1 so that scripts run by script shells can check that interactive shell is not available + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="unset PS1;"$'\n' + else + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="export PS1='${SUDO_SHELL_PS1//\'/\'\\\'\'}';"$'\n' + fi + + #if LD_PRELOAD_COMMAND is not empty, then append it to PRE_SUDO_SHELL_COMMANDS_TO_RUN + if [ ! -z "$LD_PRELOAD_COMMAND" ]; then + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="$LD_PRELOAD_COMMAND"$'\n' + fi + + #if SUDO_SHELL_RCFILE_COMMANDS is not empty, then append it to PRE_SUDO_SHELL_COMMANDS_TO_RUN + if [ ! -z "$SUDO_SHELL_RCFILE_COMMANDS" ]; then + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="$SUDO_SHELL_RCFILE_COMMANDS"$'\n' + fi + + #if SUDO_SHELL_HISTFILE_COMMANDS is not empty, then append it to PRE_SUDO_SHELL_COMMANDS_TO_RUN + if [ ! -z "$SUDO_SHELL_HISTFILE_COMMANDS" ]; then + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="$SUDO_SHELL_HISTFILE_COMMANDS"$'\n' + fi + + #if set_sudo_shell_terminal_title is enabled and stdout is available, then add command to set terminal title to PRE_SUDO_SHELL_COMMANDS_TO_RUN + if [[ "$set_sudo_shell_terminal_title" == "1" ]] && [ -t 1 ]; then + PRE_SUDO_SHELL_COMMANDS_TO_RUN+='echo -ne "\e]0;'"$SUDO_SHELL_TERMINAL_TITLE"'\a";'$'\n' + fi + + #if SUDO_SHELL_WORKING_DIR is set, then append cd command to PRE_SUDO_SHELL_COMMANDS_TO_RUN + if [ ! -z "$SUDO_SHELL_WORKING_DIR" ]; then + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="cd '${SUDO_SHELL_WORKING_DIR//\'/\'\\\'\'}';"$'\n' + fi + + #if command_type equals "su", "asu" or "path" + if [[ "$command_type" == "su" ]] || [[ "$command_type" == "asu" ]] || [[ "$command_type" == "path" ]]; then + #if ADDITIONAL_SUDO_SHELL_PRE_COMMANDS_TO_RUN is not empty, then append it to PRE_SUDO_SHELL_COMMANDS_TO_RUN + if [ ! -z "$ADDITIONAL_SUDO_SHELL_PRE_COMMANDS_TO_RUN" ]; then + PRE_SUDO_SHELL_COMMANDS_TO_RUN+="$ADDITIONAL_SUDO_SHELL_PRE_COMMANDS_TO_RUN"$'\n\n' + fi + fi + + #sudo_log 2 $'\n'"PRE_SUDO_SHELL_COMMANDS_TO_RUN=\"$PRE_SUDO_SHELL_COMMANDS_TO_RUN\"" + + return 0 + +} + +sudo_set_pre_sudo_post_shell_commands_to_run() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_pre_sudo_post_shell_commands_to_run" + + #set PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN with priority to termux paths + sudo_log_literal 2 "\nSetting PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN with priority to termux paths" + local priority="termux" + + #set priority_dependent_variables with new priority if needed + sudo_set_priority_dependent_variables "$priority" || return $? + + #set PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN + #set variables that need to be exported when running the sudo post shell + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN="" + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export TERMUX_PRIORITY_PATH='${TERMUX_PRIORITY_PATH//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export TERMUX_PRIORITY_LD_LIBRARY_PATH='${TERMUX_PRIORITY_LD_LIBRARY_PATH//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export ANDROID_PRIORITY_PATH='${ANDROID_PRIORITY_PATH//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export ANDROID_PRIORITY_LD_LIBRARY_PATH='${ANDROID_PRIORITY_LD_LIBRARY_PATH//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export PATH='${PATH_TO_EXPORT//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export LD_LIBRARY_PATH='${LD_LIBRARY_PATH_TO_EXPORT//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export PREFIX='$TERMUX_PREFIX';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export TMPDIR='$TMPDIR';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export HOME='${SUDO_POST_SHELL_HOME//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export TERM='${TERM//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export LANG='${LANG//\'/\'\\\'\'}';"$'\n' + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="export PS1='${SUDO_POST_SHELL_PS1//\'/\'\\\'\'}';"$'\n' + + #if LD_PRELOAD_COMMAND is not empty, then append it to PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN + if [ ! -z "$LD_PRELOAD_COMMAND" ]; then + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="$LD_PRELOAD_COMMAND"$'\n' + fi + + #if SUDO_POST_SHELL_RCFILE_COMMANDS is not empty, then append it to PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN + if [ ! -z "$SUDO_POST_SHELL_RCFILE_COMMANDS" ]; then + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="$SUDO_POST_SHELL_RCFILE_COMMANDS"$'\n' + fi + + #if SUDO_POST_SHELL_HISTFILE_COMMANDS is not empty, then append it to PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN + if [ ! -z "$SUDO_POST_SHELL_HISTFILE_COMMANDS" ]; then + PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN+="$SUDO_POST_SHELL_HISTFILE_COMMANDS"$'\n' + fi + + #sudo_log 2 $'\n'"PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN=\"$PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN\"" + + return 0 + +} + +sudo_set_sudo_command_path() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_command_path" + + SUDO_PATH_COMMAND="${SUDO_COMMAND[0]}" + + sudo_log 1 "SUDO_PATH_COMMAND=\"$SUDO_PATH_COMMAND\"" + + #if SUDO_PATH_COMMAND is empty + if [ -z "$SUDO_PATH_COMMAND" ]; then + sudo_log_errors "The sudo command path \"$SUDO_PATH_COMMAND\" is empty" + return 1 + fi + + #if force_set_priority_to_android_paths is enabled + if [[ "$force_set_priority_to_android_paths" == "1" ]]; then + #set priority_dependent_variables to android for PATH_TO_EXPORT used by the sudo_find_absolute_path_for_executable_and_validate function + #we do this now and later in the sudo_set_pre_sudo_shell_commands_to_run function as well once the final SUDO_PATH_COMMAND has been set + sudo_set_priority_dependent_variables "android" || return $? + fi + + #find the absolute path for SUDO_PATH_COMMAND that should be used and validate it + #SUDO_PATH_COMMAND is either considered an absolute path, otherwise searched for in current working directory or in all the paths in $PATH_TO_EXPORT variable + #if use_root_for_path_search_and_validation is enabled + if [[ "$use_root_for_path_search_and_validation" == "1" ]]; then + sudo_unset_pre_su_variables + SUDO_COMMAND_ABSOLUTE_PATH="$($SU_ENV_COMMAND "source '${SUDO_SCRIPT_PATH//\'/\'\\\'\'}'; sudo_find_absolute_path_for_executable_and_validate \"SUDO_COMMAND_ABSOLUTE_PATH\" \"SUDO_PATH_COMMAND\" '${SUDO_PATH_COMMAND//\'/\'\\\'\'}' '${PATH_TO_EXPORT//\'/\'\\\'\'}' \"$TERMUX_PREFIX\" \"$TERMUX_HOME\" 1" < /dev/null)" + return_value=$? + sudo_set_post_su_variables + else + sudo_find_absolute_path_for_executable_and_validate "SUDO_COMMAND_ABSOLUTE_PATH" "SUDO_PATH_COMMAND" "$SUDO_PATH_COMMAND" "$PATH_TO_EXPORT" "$TERMUX_PREFIX" "$TERMUX_HOME" 0 + return_value=$? + fi + if [ $return_value -ne 0 ] && [ $return_value -ne 112 ]; then + sudo_log_errors "Failure while running \"sudo_find_absolute_path_for_executable_and_validate\"" + return $return_value + elif [ $return_value -eq 112 ]; then + sudo_log_errors "Command \"$SUDO_PATH_COMMAND\" not found" + sudo_log_errors "Check your spelling and try again" + return 1 + fi + + SUDO_PATH_COMMAND="$SUDO_COMMAND_ABSOLUTE_PATH" + + sudo_log 1 "SUDO_PATH_COMMAND_UPDATED=\"$SUDO_PATH_COMMAND\"" + + #if SUDO_PATH_COMMAND is not a valid absolute path + if [[ ! "$SUDO_PATH_COMMAND" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_PATH_COMMAND \"$SUDO_PATH_COMMAND\" is not a valid absolute path" + return 1 + fi + + return 0 + +} + +sudo_set_sudo_path_command() { + + local return_value + + sudo_log_literal 2 "\n\n\nRunning sudo_set_sudo_path_command" + + #if SUDO_COMMAND is not set + if [ ${#SUDO_COMMAND[@]} -eq 0 ]; then + sudo_log_errors "SUDO_COMMAND is not set while running \"sudo_set_sudo_path_command\"" + return 1 + fi + + sudo_log 2 "SUDO_COMMAND_ARRAY_COUNT=\"${#SUDO_COMMAND[@]}\"" + + sudo_log 2 $'\n'"SUDO_PATH_COMMAND=\`$SUDO_PATH_COMMAND\`" + + SUDO_PATH_COMMAND_TO_RUN="" + + #if "su" binary is being run, then unset LD_PRELOAD and LD_LIBRARY_PATH + #if only "su" was passed without the '-a' option and current directory + #did not have the "su" binary, then the wrapper script by termux at + #$PREFIX/bin/su would ideally have been chosen by + #the sudo_set_sudo_command_path function as SUDO_PATH_COMMAND, in + #which case the wrapper script would ideally also automatically + #unset the variables + if [[ "$SUDO_PATH_COMMAND" == *"/su" ]]; then + SUDO_PATH_COMMAND_TO_RUN="unset LD_PRELOAD; unset LD_LIBRARY_PATH; " + fi + + #if SUDO_COMMAND argument size is 1, then only a path was passed to be executed + if [ ${#SUDO_COMMAND[@]} -eq 1 ]; then + + SUDO_PATH_COMMAND_TO_RUN+="'${SUDO_PATH_COMMAND//\'/\'\\\'\'}'" + #else path was passed as $1, followed by arguments that should be passed to the path + else + SUDO_COMMAND=("${SUDO_COMMAND[@]:1}") #remove first element since its the unparsed SUDO_PATH_COMMAND + + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_log 2 "Processing SUDO_PATH_COMMAND_ARGS array as per RUN_COMMAND intent rules" + fi + + for i in "${!SUDO_COMMAND[@]}"; do + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + #replace any COMMA_ALTERNATIVE characters with normal commas `,` that exist in {SUDO_COMMAND[$i]} + if [[ "${SUDO_COMMAND[$i]}" == *"$COMMA_ALTERNATIVE"* ]]; then + sudo_log 2 "COMMA_ALTERNATIVE found in SUDO_PATH_COMMAND_ARG $((i+1))" + sudo_replace_comma_alternative_chars_with_commas_in_string "SUDO_COMMAND[$i]" "${SUDO_COMMAND[$i]}" + fi + fi + + #if disable_arguments_logging is not enabled + if [[ "$disable_arguments_logging" != "1" ]]; then + sudo_log 2 "SUDO_PATH_COMMAND_ARG $((i+1))=\`${SUDO_COMMAND[$i]}\`"$'\n' + fi + done + + printf -v "SUDO_PATH_COMMAND_ARGS" "%q " "${SUDO_COMMAND[@]}" + SUDO_PATH_COMMAND_ARGS="${SUDO_PATH_COMMAND_ARGS% }" #remove trailing space + + SUDO_PATH_COMMAND_ARGS_LENGTH="${#SUDO_PATH_COMMAND_ARGS}" + sudo_log 2 "SUDO_PATH_COMMAND_ARGS_LENGTH=\"$SUDO_PATH_COMMAND_ARGS_LENGTH\"" + + + #The `path` would not contribute much to crossing ARG_MAX since its only a path to a file, unless the path is too long + #The ARG_MAX would be a concern only when arguments to `path` are passed via process substitution to the sudo script + #and not as a direct argument since in that case ARG_MAX would have crossed at the point instead + #if SUDO_PATH_COMMAND_ARGS_LENGTH is greater than SUDO_ARG_MAX_SAFE_LIMIT + if [[ "$SUDO_PATH_COMMAND_ARGS_LENGTH" -gt "$SUDO_ARG_MAX_SAFE_LIMIT" ]]; then + sudo_log 1 "Warning! The SUDO_PATH_COMMAND_ARGS_LENGTH \"$SUDO_PATH_COMMAND_ARGS_LENGTH\" is greater than $((SUDO_ARG_MAX_SAFE_LIMIT / 1024))KB. This may cause an \"Argument list too long\" exception." + fi + + #pass SUDO_PATH_COMMAND_ARGS to SUDO_PATH_COMMAND + SUDO_PATH_COMMAND_TO_RUN+=" '${SUDO_PATH_COMMAND//\'/\'\\\'\'}' $SUDO_PATH_COMMAND_ARGS" + fi + + #SUDO_PATH_COMMAND_TO_RUN and SUDO_SCRIPT_COMMAND_TO_RUN needs to be passed to the BASH_SHELL inside su + #by the sudo_run function + #we pass the SUDO_PATH_COMMAND_TO_RUN to bash instead of directly passing commands to 'su -c' because + #LD_PRELOAD does not work in the later case and scripts that are using #!/usr/bin/* shebang instead of termux + #shebang fail + #check sudo_set_sudo_script_command function for more details on why fd is used + sudo_get_unsed_file_descriptor "SUDO_SCRIPT_FD" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$SUDO_SCRIPT_FD" =~ $valid_number_regex ]]; then + sudo_log_errors "Failure while getting an unused file descriptor to write SUDO_PATH_COMMAND_TO_RUN" + sudo_log_errors "SUDO_SCRIPT_FD = \"$SUDO_SCRIPT_FD\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + #android fd directory is /proc/self/fd + #using $$ instead of self so that su can access it + SUDO_PATH_FD_PATH="/proc/$$/fd/$SUDO_SCRIPT_FD" + + #use printf to write to SUDO_SCRIPT_FD + sudo_log_literal 2 "Writing SUDO_PATH_COMMAND_TO_RUN to SUDO_PATH_FD_PATH \"$SUDO_PATH_FD_PATH\"" + eval "exec $SUDO_SCRIPT_FD<" <(printf "%s" "$SUDO_PATH_COMMAND_TO_RUN") + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while writing SUDO_PATH_COMMAND_TO_RUN to SUDO_SCRIPT_FD \"$SUDO_SCRIPT_FD\"" + return $return_value + fi + + return 0 + +} + +sudo_set_sudo_script_command() { + + local return_value + + sudo_log_literal 2 "\n\n\nRunning sudo_set_sudo_script_command" + + SUDO_SCRIPT_COMMAND_TO_RUN=$'\n' + + #append PRE_SUDO_SHELL_COMMANDS_TO_RUN to SUDO_SCRIPT_COMMAND_TO_RUN + SUDO_SCRIPT_COMMAND_TO_RUN+="$PRE_SUDO_SHELL_COMMANDS_TO_RUN"$'\n\n' + + #if ADDITIONAL_SUDO_SHELL_PRE_COMMANDS_TO_RUN is not empty, then append it to SUDO_SCRIPT_COMMAND_TO_RUN + if [ ! -z "$ADDITIONAL_SUDO_SHELL_PRE_COMMANDS_TO_RUN" ]; then + SUDO_SCRIPT_COMMAND_TO_RUN+="$ADDITIONAL_SUDO_SHELL_PRE_COMMANDS_TO_RUN"$'\n\n' + fi + + sudo_log 2 "SUDO_COMMAND_ARRAY_COUNT=\"${#SUDO_COMMAND[@]}\"" + + #if a SUDO_CORE_SCRIPT or arguments passed and SUDO_CORE_SCRIPT is not empty + #the SUDO_CORE_SCRIPT can optionally not be passed or passed as an empty string so that other "features" + #of the "script" command_type can still be used + if [ ${#SUDO_COMMAND[@]} -ne 0 ] && [ ! -z "${SUDO_COMMAND[0]}" ]; then + + sudo_log 2 "" + + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_log 2 "Processing SUDO_CORE_SCRIPT and SUDO_CORE_SCRIPT_COMMAND_ARGS array as per RUN_COMMAND intent rules" + fi + + for i in "${!SUDO_COMMAND[@]}"; do + #if its the SUDO_CORE_SCRIPT argument and decode_core_script_content or core_script_is_path_to_script_file is enabled + if [ "$i" -eq 0 ] && ([[ "$decode_core_script_content" == "1" ]] || [[ "$core_script_is_path_to_script_file" == "1" ]]); then + #The SUDO_COMMAND[1] contains encoded data or a path to a file so no need to process it + continue + fi + + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + #replace any COMMA_ALTERNATIVE characters with normal commas `,` that exist in {SUDO_COMMAND[$i]} + if [[ "${SUDO_COMMAND[$i]}" == *"$COMMA_ALTERNATIVE"* ]]; then + #if its the SUDO_CORE_SCRIPT argument + if [ "$i" -eq 0 ]; then + sudo_log 2 "COMMA_ALTERNATIVE found in SUDO_CORE_SCRIPT" + else + sudo_log 2 "COMMA_ALTERNATIVE found in SUDO_CORE_SCRIPT_COMMAND_ARG $((i+1))" + fi + sudo_replace_comma_alternative_chars_with_commas_in_string "SUDO_COMMAND[$i]" "${SUDO_COMMAND[$i]}" + fi + fi + + #if disable_arguments_logging is not enabled + if [[ "$disable_arguments_logging" != "1" ]]; then + #if its the SUDO_CORE_SCRIPT argument + if [ "$i" -eq 0 ]; then + sudo_log 2 "SUDO_CORE_SCRIPT=\`${SUDO_COMMAND[$i]}\`"$'\n' + #else SUDO_CORE_SCRIPT was passed as $1, followed by arguments that should be passed to the script + else + sudo_log 2 "SUDO_CORE_SCRIPT_COMMAND_ARG $((i+1))=\`${SUDO_COMMAND[$i]}\`"$'\n' + fi + fi + done + + #set the first argument as SUDO_CORE_SCRIPT + SUDO_CORE_SCRIPT="${SUDO_COMMAND[0]}" + + #if SUDO_CORE_SCRIPT_TEMP_FILENAME is empty, then use default + if [ -z "$SUDO_CORE_SCRIPT_TEMP_FILENAME" ]; then + SUDO_CORE_SCRIPT_TEMP_FILENAME="sudo_core_script" + else + #else if user passed the filename with --script-name' option, then + #strip longest match of */ from start, to remove any directory path passed and keep only the basename + SUDO_CORE_SCRIPT_TEMP_FILENAME="${SUDO_CORE_SCRIPT_TEMP_FILENAME##*/}" + #if empty, then use default + SUDO_CORE_SCRIPT_TEMP_FILENAME="${SUDO_CORE_SCRIPT_TEMP_FILENAME:=sudo_core_script}" + fi + + #start a new SUDO_SHELL script shell and pass SUDO_CORE_SCRIPT to it + #the SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL will contain the SUDO_CORE_SCRIPT argument that will be passed to the SUDO_SHELL + #the way the SUDO_CORE_SCRIPT argument will be passed will depend on script shell capabilities and the options set + #sudo_set_script_argument_for_script_shell shell_basename heredoc_word heredoc_word_quote script_is_path_to_script_file force_use_temp_script_file decode_script_content script_file_label script_file_name script_file_path/script_content + sudo_set_script_argument_for_script_shell "$SUDO_SHELL_BASENAME" "SUDO_CORE_SCRIPT_EOF" "'" "$core_script_is_path_to_script_file" "$force_use_temp_script_file_for_core_script" "$decode_core_script_content" "SUDO_CORE_SCRIPT" "$SUDO_CORE_SCRIPT_TEMP_FILENAME" "$SUDO_CORE_SCRIPT" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to create SUDO_CORE_SCRIPT argument commands to pass them to SUDO_SHELL \"$SUDO_SHELL\"" + return $return_value + fi + + #pass the SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL set by sudo_set_script_argument_for_script_shell to SUDO_SHELL + printf -v "SUDO_SHELL_COMMAND_STRING" "$SUDO_SHELL_COMMAND_PRINT_FORMAT" "${SUDO_SHELL_COMMAND[@]}" + SUDO_CORE_SCRIPT_COMMAND_TO_RUN="$SUDO_SHELL_COMMAND_STRING$SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL" + + #if arguments need to be passed to SUDO_CORE_SCRIPT + if [ ${#SUDO_COMMAND[@]} -gt 1 ]; then + SUDO_COMMAND=("${SUDO_COMMAND[@]:1}") #remove first element since its the script + + printf -v "SUDO_CORE_SCRIPT_COMMAND_ARGS" "%q " "${SUDO_COMMAND[@]}" + SUDO_CORE_SCRIPT_COMMAND_ARGS="${SUDO_CORE_SCRIPT_COMMAND_ARGS% }" #remove trailing space + + SUDO_CORE_SCRIPT_COMMAND_ARGS_LENGTH="${#SUDO_CORE_SCRIPT_COMMAND_ARGS}" + sudo_log 2 "SUDO_CORE_SCRIPT_COMMAND_ARGS_LENGTH=\"$SUDO_CORE_SCRIPT_COMMAND_ARGS_LENGTH\"" + + #The `core_script` would not contribute much to crossing ARG_MAX since its either passed with process substitution or + #as a path to a file storing it, unless the path is too long + #The ARG_MAX would be a concern only when arguments to `core_script` are passed via process substitution to the sudo script + #and not as a direct argument since in that case ARG_MAX would have crossed at the point instead + #if SUDO_CORE_SCRIPT_COMMAND_ARGS_LENGTH is greater than SUDO_ARG_MAX_SAFE_LIMIT + if [[ "$SUDO_CORE_SCRIPT_COMMAND_ARGS_LENGTH" -gt "$SUDO_ARG_MAX_SAFE_LIMIT" ]]; then + sudo_log 1 "Warning! The SUDO_CORE_SCRIPT_COMMAND_ARGS_LENGTH \"$SUDO_CORE_SCRIPT_COMMAND_ARGS_LENGTH\" is greater than $((SUDO_ARG_MAX_SAFE_LIMIT / 1024))KB. This may cause an \"Argument list too long\" exception." + fi + + #pass the arguments to the SUDO_SHELL after the SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=" $SUDO_CORE_SCRIPT_COMMAND_ARGS" + fi + + #if SUDO_CORE_SCRIPT_REDIRECT_MODE or SUDO_SHELL_STDIN_STRING is set or + #disable_stdin_for_core_script or run_core_script_in_background is enabled + if [ ! -z "$SUDO_CORE_SCRIPT_REDIRECT_MODE" ] || \ + [ ! -z "$SUDO_SHELL_STDIN_STRING" ] || \ + [[ "$disable_stdin_for_core_script" == "1" ]] || \ + [[ "$run_core_script_in_background" == "1" ]]; then + #surround SUDO_CORE_SCRIPT_COMMAND_TO_RUN with command grouping + SUDO_CORE_SCRIPT_COMMAND_TO_RUN="{"$'\n\n'"$SUDO_CORE_SCRIPT_COMMAND_TO_RUN"$'\n\n''}' + + #if SUDO_CORE_SCRIPT_REDIRECT_MODE is set + if [ ! -z "$SUDO_CORE_SCRIPT_REDIRECT_MODE" ]; then + + #if SUDO_CORE_SCRIPT_REDIRECT_MODE equal "0", then redirect stderr to stdout + if [[ "$SUDO_CORE_SCRIPT_REDIRECT_MODE" == "0" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' 2>&1' + + #elif SUDO_CORE_SCRIPT_REDIRECT_MODE equal "1", then redirect stdout to stderr + elif [[ "$SUDO_CORE_SCRIPT_REDIRECT_MODE" == "1" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' 1>&2' + + #elif SUDO_CORE_SCRIPT_REDIRECT_MODE equal "2", then redirect stdout to /dev/null + elif [[ "$SUDO_CORE_SCRIPT_REDIRECT_MODE" == "2" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' 1>/dev/null' + + #elif SUDO_CORE_SCRIPT_REDIRECT_MODE equal "3", then redirect stderr to /dev/null + elif [[ "$SUDO_CORE_SCRIPT_REDIRECT_MODE" == "3" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' 2>/dev/null' + + #elif SUDO_CORE_SCRIPT_REDIRECT_MODE equal "4", then redirect both stdout and stderr to /dev/null + elif [[ "$SUDO_CORE_SCRIPT_REDIRECT_MODE" == "4" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' &>/dev/null' + + #elif SUDO_CORE_SCRIPT_REDIRECT_MODE equal "5", then redirect stderr to stdout and redirect stdout to stderr + elif [[ "$SUDO_CORE_SCRIPT_REDIRECT_MODE" == "5" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' 3>&2 2>&1 1>&3' + + #elif SUDO_CORE_SCRIPT_REDIRECT_MODE equal "6", then redirect stderr to stdout and redirect stdout to /dev/null + elif [[ "$SUDO_CORE_SCRIPT_REDIRECT_MODE" == "6" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' 2>&1 1>/dev/null' + + #else append SUDO_CORE_SCRIPT_REDIRECT_MODE to SUDO_CORE_SCRIPT_COMMAND_TO_RUN + else + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=" $SUDO_CORE_SCRIPT_REDIRECT_MODE" + + fi + fi + + #if SUDO_SHELL_STDIN_STRING is set + if [ ! -z "$SUDO_SHELL_STDIN_STRING" ]; then + #if SUDO_SHELL_BASENAME is in SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT + if [[ " $SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT " == *" $SUDO_SHELL_BASENAME "* ]]; then + #pass SUDO_SHELL_STDIN_STRING as stdin with a herestring to SUDO_SHELL + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' <<<'"'${SUDO_SHELL_STDIN_STRING//\'/\'\\\'\'}'" + else + #pass SUDO_SHELL_STDIN_STRING as stdin with process substitution to SUDO_SHELL + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' < <(printf "%s" '"'${SUDO_SHELL_STDIN_STRING//\'/\'\\\'\'}'"')' + fi + else + #if disable_stdin_for_core_script is enabled, then redirect stdin to /dev/null + if [[ "$disable_stdin_for_core_script" == "1" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' < /dev/null' + fi + fi + + #if run_core_script_in_background is enabled, then start SUDO_CORE_SCRIPT in background + if [[ "$run_core_script_in_background" == "1" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=' &' + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=$'\n''export SUDO_SCRIPT_PID=$!;' + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=$'\n''export SUDO_SCRIPT_EXIT_CODE=0;' + fi + + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=$'\n\n' + fi + + #if run_core_script_in_background is not enabled + if [[ "$run_core_script_in_background" != "1" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+=$'\n''export SUDO_SCRIPT_EXIT_CODE=$?;'$'\n\n' + + #if exit_early_if_core_script_fails is enabled, then exit if SUDO_SCRIPT_EXIT_CODE does not equal 0 + if [[ "$exit_early_if_core_script_fails" == "1" ]]; then + SUDO_CORE_SCRIPT_COMMAND_TO_RUN+='[ $SUDO_SCRIPT_EXIT_CODE -ne 0 ] && exit $SUDO_SCRIPT_EXIT_CODE'$'\n\n' + fi + fi + + #append SUDO_CORE_SCRIPT_COMMAND_TO_RUN to SUDO_SCRIPT_COMMAND_TO_RUN + SUDO_SCRIPT_COMMAND_TO_RUN+="$SUDO_CORE_SCRIPT_COMMAND_TO_RUN" + + else + #append dummy SUDO_SCRIPT_EXIT_CODE to SUDO_SCRIPT_COMMAND_TO_RUN + SUDO_SCRIPT_COMMAND_TO_RUN+='export SUDO_SCRIPT_EXIT_CODE=0;'$'\n\n' + fi + + #if ADDITIONAL_SUDO_SHELL_POST_COMMANDS_TO_RUN is not empty, then append it to SUDO_SCRIPT_COMMAND_TO_RUN + if [ ! -z "$ADDITIONAL_SUDO_SHELL_POST_COMMANDS_TO_RUN" ]; then + SUDO_SCRIPT_COMMAND_TO_RUN+="$ADDITIONAL_SUDO_SHELL_POST_COMMANDS_TO_RUN"$'\n\n' + fi + + #if go_back_to_last_activity_after_running_core_script is enabled, then simulate double back button press to go to the last activity, first to close keyboard and second to close terminal session + if [[ "$go_back_to_last_activity_after_running_core_script" == "1" ]]; then + SUDO_SCRIPT_COMMAND_TO_RUN+='$(export PATH="$ANDROID_PRIORITY_PATH"; export LD_LIBRARY_PATH="$ANDROID_PRIORITY_LD_LIBRARY_PATH"; unset LD_PRELOAD; input keyevent KEYCODE_BACK; input keyevent KEYCODE_BACK;);'$'\n\n' + #if go_to_launcher_activity_after_running_core_script is enabled, then simulate home button press to go to the launcher activity + elif [[ "$go_to_launcher_activity_after_running_core_script" == "1" ]]; then + SUDO_SCRIPT_COMMAND_TO_RUN+='$(export PATH="$ANDROID_PRIORITY_PATH"; export LD_LIBRARY_PATH="$ANDROID_PRIORITY_LD_LIBRARY_PATH"; unset LD_PRELOAD; am start --user 0 -a android.intent.action.MAIN -c android.intent.category.HOME &>/dev/null;);'$'\n\n' + fi + + #if clear_shell_after_running_core_script is enabled, then add clear command + if [[ "$clear_shell_after_running_core_script" == "1" ]]; then + SUDO_SCRIPT_COMMAND_TO_RUN+="clear"$'\n' + fi + + #if run_interactive_post_sudo_shell_after_running_core_script is enabled, start a new interactive post shell + #we do not check if stdout and stdin are available since sudo could be piped or redirected and may not be connected to a tty + #the -t 0 and -t 1 checks fail if sudo is run in a simple subshell in android 10 but not on 7 + if [[ "$run_interactive_post_sudo_shell_after_running_core_script" == "1" ]]; then + #append PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN + SUDO_SCRIPT_COMMAND_TO_RUN+="$PRE_SUDO_POST_SHELL_COMMANDS_TO_RUN"$'\n' + + #if ADDITIONAL_SUDO_POST_SHELL_PRE_COMMANDS_TO_RUN is not empty, then append it to SUDO_SCRIPT_COMMAND_TO_RUN + if [ ! -z "$ADDITIONAL_SUDO_POST_SHELL_PRE_COMMANDS_TO_RUN" ]; then + SUDO_SCRIPT_COMMAND_TO_RUN+="$ADDITIONAL_SUDO_POST_SHELL_PRE_COMMANDS_TO_RUN"$'\n' + fi + + printf -v "SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING" "$SUDO_POST_SHELL_INTERACTIVE_COMMAND_PRINT_FORMAT" "${SUDO_POST_SHELL_INTERACTIVE_COMMAND[@]}" + SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING="${SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING% }" #remove trailing space + + #if SUDO_POST_SHELL_STDIN_STRING is set + if [ ! -z "$SUDO_POST_SHELL_STDIN_STRING" ]; then + #surround SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING with command grouping + SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING="{"$'\n\n'"$SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING"$'\n\n''}' + + #if SUDO_POST_SHELL_BASENAME is in SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT + if [[ " $SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT " == *" $SUDO_POST_SHELL_BASENAME "* ]]; then + #pass SUDO_POST_SHELL_STDIN_STRING as stdin with a herestring to SUDO_POST_SHELL + SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING+=' <<<'"'${SUDO_POST_SHELL_STDIN_STRING//\'/\'\\\'\'}'" + else + #pass SUDO_POST_SHELL_STDIN_STRING as stdin with process substitution to SUDO_POST_SHELL + SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING+=' < <(printf "%s" '"'${SUDO_POST_SHELL_STDIN_STRING//\'/\'\\\'\'}'"')' + fi + fi + + SUDO_SCRIPT_COMMAND_TO_RUN+="$SUDO_POST_SHELL_INTERACTIVE_COMMAND_STRING"$'\n' + + #if ADDITIONAL_SUDO_POST_SHELL_POST_COMMANDS_TO_RUN is not empty, then append it to SUDO_SCRIPT_COMMAND_TO_RUN + if [ ! -z "$ADDITIONAL_SUDO_POST_SHELL_POST_COMMANDS_TO_RUN" ]; then + SUDO_SCRIPT_COMMAND_TO_RUN+="$ADDITIONAL_SUDO_POST_SHELL_POST_COMMANDS_TO_RUN"$'\n' + fi + else + #exit with exit code of SUDO_CORE_SCRIPT + SUDO_SCRIPT_COMMAND_TO_RUN+='exit $SUDO_SCRIPT_EXIT_CODE'$'\n' + fi + + + + #set SUDO_SCRIPT_COMMAND_TRAPS and SUDO_SCRIPT_COMMAND_TRAPS_REMOVE_TEMP_DIRECTORY_FUNCTION + sudo_create_script_command_traps + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to create traps commands for SUDO_TEMP_DIRECTORY \"$SUDO_TEMP_DIRECTORY\"" + return $return_value + fi + + #if SUDO_SCRIPT_COMMAND_TRAPS is set + if [ ! -z "$SUDO_SCRIPT_COMMAND_TRAPS" ]; then + #if a script shell that doesn't support process substitution is being used or '-f' is passed, then + #sudo_set_script_argument_for_script_shell_for_sudo will store temp core_script file at SUDO_CORE_SCRIPT_TEMP_FILENAME in SUDO_TEMP_DIRECTORY + #add traps at start of SUDO_SCRIPT_COMMAND_TO_RUN to remove the SUDO_TEMP_DIRECTORY when the script exits + SUDO_SCRIPT_COMMAND_TO_RUN=$'\n'"$SUDO_SCRIPT_COMMAND_TRAPS"$'\n'"$SUDO_SCRIPT_COMMAND_TO_RUN" + fi + + + + #SUDO_SCRIPT_COMMAND_TO_RUN cannot be passed to the bash shell inside su using process substitution + #since the "self" in "proc/self/fd/#" passed by process substitution is for the sudo script process + #and not for the bash process inside su + #so when bash shell inside su tries to read from it, then there will be errors like + #"/proc/self/fd/63: No such file or directory" + #hence we manually create a file descriptor and pass its path to su or to be more specific to the bash shell + #for the path and script commands + #/system/xbin/su --shell="/data/data/com.termux/files/usr/bin/bash" --preserve-environment -c "bash --noprofile --norc" <(echo 'echo 1') + + #the method that is used for passing data to su using file descriptors in sudo_run function and elsewhere is the following + #get an unsed file descriptor for the current sudo script process + #manually set fd path in /proc/$$/fd since that is where the fd are created in android + #$$ returns pid of current process + #then run something like `exec 3< <(printf "%s" "string")` + #this will create a pseudo pipe file descriptor and attach stdout of printf to the file descriptor 3 + #this file descriptor will exist at /proc/$$/fd/3 + #this basically creates the file descriptor and "saves" the output of printf before su is started + #so /proc/self/fd issues do not occur + #we pass this path to whatever requires it inside su, like bash or cat + #the path will of course be for current sudo script process and not for bash because of $$ + #but since bash is being run with root it will be able to read from it + #the file descriptor will be like a read only stream like in pipes. seeking forward/backward is not possibly + #and once data has been read by another process, it is gone and cannot be read again from the start + #credits and details: https://stackoverflow.com/a/20018118/14686958 by Jo So + + #there are other reasons as well why this method is being used other than the /proc/self/fd issues + #1) This method is faster than heredocs and herestrings since data is processed only in memory + # and does not create temp files on the disk increasing execution time like the later. The data is of course + # buffered with a likely limit of 64KB but that's still large enough not to cause problems for average script text passing + #2) The temp files created by heredocs and herestrings are in TMPDIR which in termux's case is $PREFIX/usr/tmp. + # The TMPDIR is relatively less secure since non-root processes can access it and ideally shouldn't be used due to + # potential risk of privilege escalation or private data leakage. Although any process in termux context could get + # su access if termux app has been granted root access but other apps granted SAF access can by default only access + # $TERMUX_HOME/ and not $PREFIX/. + # You can confirm usage of TMPDIR by running: + # ``` + # #for herestrings + # sleep 3 <<<"here string" & lsof -w -p $! | grep 0r + # + # #for heredocs + # sleep 3 <<EOF & + # here doc + # EOF + # lsof -w -p $! | grep 0r + # ``` + #3) The script command_type may require stdin to be usable by the scripts + # If heredocs and herestrings are used, then they will be read by the bash shell through the stdin + # which will prevent the script shells to use it for user input and they may even read characters from + # the script itself as stdin in some cases + # Hence process substitution should be used which leaves stdin free, but of course that is not possible due + # to /proc/self/fd issues due to su usage. + # The other alternative is to create another temp file in SUDO_TEMP_DIRECTORY and store the SUDO_SCRIPT_COMMAND_TO_RUN + # in it and pass the path to bash. This will of course increase execution time significantly due to creation of + # the SUDO_TEMP_DIRECTORY directory on every run even if sudo_core_script temp file is not to be created + # and also due to another call to the su shell for the creation of the another temp file. + # Another hacky alternative that will work is to solve the /proc/self/fd issue itself + # That can be done by guessing the real path of fd inside the su shell and pass that to bash instead + # That can be done by running: + # ``` + # fd_number="$(echo <(echo "") | sed -E 's|/proc/self/fd/([0-9]+)|\1|')" + # /system/xbin/su --shell="/data/data/com.termux/files/usr/bin/bash" --preserve-environment -c "bash_shell_pid=$$; "'su_shell_pid=$(pgrep -P $bash_shell_pid); bash /proc/$su_shell_pid/fd/'"$fd_number" <(echo 'echo 1') + # ``` + # Use the path to the 'su' installed on your device if its not at '/system/xbin/su' + # The first line will just get the fd number that is chosen by default by bash when it uses process substitution which + # which is likely going to be 63 + # The second line is the shorter version of SU_RUN_COMMAND used by the sudo script + # The single and double quoting is extremely important to make sure some things are + # run in the local sudo script bash shell and others inside the su shell + # note that the local bash shell is of the sudo script and is different from the bash shell that is inside the su shell + # First we get store the pid of the local bash shell in bash_shell_pid. Note the double quotes so that $$ expands locally. + # Then we get pid of su shell by finding the process whose parent pid matches the bash_shell_pid. + # Since there are no background processes run by the sudo script, pgrep should return a single pid. + # Then we pass the /proc/su_shell_pid/fd/fd_number path to bash directly that the `<(echo 'echo 1')` process substitution will create + # The su_shell_pid is inside single quotes so it expands inside the su shell and fd_number is inside double quotes so it expands locally. + # Despite the /proc/self/fd/63 argument passed due to process substitution, bash will read from the path manually passed. + # Note that we cannot create the path using $PPID variable because multiple processes are created by the su shell and PPID will + # not match su_shell_pid. + # If the $PREFIX/bin/su is directly used by running `su --shell...` instead of full path, then another process is created + # because su in termux bin is actually a wrapper script and process substitution path will be created for it instead, it may even + # give errors while executing commands. + # This method is however not used because its a bit hacky but could work, but would require multiple device and multiple su implementation testing. + # Another reason this isn't used is because normally, the sudo binary of linux distros will close all open file descriptors other + # than standard input, standard output and standard error when its called for security reasons to prevent child processes + # from getting access to file descriptors of the parent processes that were not intended for it. + # Some su binaries behave in the same way. There are however not closed by SuperSU v2.82 as shown by the above test and neither by magisk v21.1 + # but other android su implementations may close the file descriptors, breaking this method. + # + # To monitor the file descriptors 63 opened by child processes, run the following + # ``` + # #drop to a root shell so that root processes can also be monitored + # sudo su + # #run a background infinite loop that passes a comma separated list of pids returned by ps to ls using brace expansion that will list + # #all files in /proc/{pids}/fd paths and then grep only fds matching 63 and output their pid to stdout which is captured by $pid + # #if $pid is set, then ps is used to display pid,ppid,cmd of all pids stored in the $pid variable + # (while true; do pid="$(eval "ls --color=never -d -1 /proc/{$(ps --no-headers -o pid -g | sed -z -e 's/\n/,/g' -e 's/ //g' -e 's/,$//')}/fd/* 2>/dev/null" | grep "/fd/63" | sed -E 's|/proc/([0-9]+)/fd/.*|\1|g')"; [ ! -z "$pid" ] && ps -wwo pid,ppid,cmd $(echo "$pid" | sed -z -E 's/[ \n\t]+/ /'); done) & + # #store the pid of the background process so that it can be killed later + # while_pid=$! + # #then drop to another root shell + # sudo su + # #then run the su command with an added sleep command for background monitoring to work, you may want to increase the sleep time + # #ideally two commands would be shown by ps that created fd 63, one for the local bash interactive shell of the sudo su command + # #and the other for the newly created su shell since the fd will be copied during the fork + # fd_number="$(echo <(echo "") | sed -E 's|/proc/self/fd/([0-9]+)|\1|')" + # /system/xbin/su --shell="/data/data/com.termux/files/usr/bin/bash" --preserve-environment -c "bash_shell_pid=$$; "'su_shell_pid=$(pgrep -P $bash_shell_pid); sleep 0.3; bash /proc/$su_shell_pid/fd/'"$fd_number" <(echo 'echo 1') + # #exit the second root shell once done + # exit + # #kill background process, do not leave it running since it will consume lots of resources and may slow down the system + # kill $while_pid + # ``` + # #there are probably other methods using lsof etc. + + sudo_get_unsed_file_descriptor "SUDO_SCRIPT_FD" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$SUDO_SCRIPT_FD" =~ $valid_number_regex ]]; then + sudo_log_errors "Failure while getting an unused file descriptor to write SUDO_SCRIPT_COMMAND_TO_RUN" + sudo_log_errors "SUDO_SCRIPT_FD = \"$SUDO_SCRIPT_FD\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + #android fd directory is /proc/self/fd + #using $$ instead of self so that su can access it + SUDO_SCRIPT_FD_PATH="/proc/$$/fd/$SUDO_SCRIPT_FD" + + #use printf to write to SUDO_SCRIPT_FD + sudo_log_literal 2 "\nWriting SUDO_SCRIPT_COMMAND_TO_RUN to SUDO_SCRIPT_FD_PATH \"$SUDO_SCRIPT_FD_PATH\"" + eval "exec $SUDO_SCRIPT_FD<" <(printf "%s" "$SUDO_SCRIPT_COMMAND_TO_RUN") + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while writing SUDO_SCRIPT_COMMAND_TO_RUN to SUDO_SCRIPT_FD \"$SUDO_SCRIPT_FD\"" + return $return_value + fi + + return 0 + +} + +sudo_check_if_setting_up_sudo_temp_directory_is_required() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_check_if_setting_up_sudo_temp_directory_is_required" + + #if SUDO_SHELL_BASENAME is in SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT + #or force_use_temp_script_file is enabled + #then the script file will need to be stored in a temp script file in SUDO_TEMP_DIRECTORY + #this logic must be same as that in sudo_set_script_argument_for_script_shell function + #so that SUDO_TEMP_DIRECTORY has already been created if needed + #and the script_file path can be set correctly and be passed to the script shell + #its not directly created in the other function to reduce a call to the su shell + if [[ " $SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT " == *" $SUDO_SHELL_BASENAME "* ]] || [[ "$force_use_temp_script_file_for_core_script" == "1" ]]; then + #enable SETUP_SUDO_TEMP_DIRECTORY + sudo_log 2 "SETUP_SUDO_TEMP_DIRECTORY enabled" + SETUP_SUDO_TEMP_DIRECTORY=1 + fi + + return 0 + +} + +#sudo_set_script_argument_for_script_shell shell_basename heredoc_word heredoc_word_quote script_is_path_to_script_file force_use_temp_script_file decode_script_content script_file_label script_file_name script_file_path/script_content +sudo_set_script_argument_for_script_shell() { + + local return_value + + #if parameter count is not 9 + if [ $# -ne 9 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_set_script_argument_for_script_shell\"" + return 1 + fi + + local shell_basename="$1" + local heredoc_word="$2" + local heredoc_word_quote="$3" + local script_is_path_to_script_file="$4" + local force_use_temp_script_file="$5" + local decode_script_content="$6" + local script_file_label="$7" + local script_file_name="$8" + + #if shell_basename is not in any of the SUDO_SUPPORTED_SCRIPT_SHELLS, then exit with error + if [[ "$shell_basename" == *' '* ]] || [[ " $SUDO_SUPPORTED_SCRIPT_SHELLS " != *" $shell_basename "* ]]; then + sudo_log_errors "The shell_basename \"$shell_basename\" while running \"sudo_set_script_argument_for_script_shell\" is not supported. It must be one of \"$SUDO_SUPPORTED_SCRIPT_SHELLS\"" + return 1 + fi + + SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL="" + + #if script_is_path_to_script_file is enabled, then just pass script_file_path as the argument to the script shell + if [[ "$script_is_path_to_script_file" == "1" ]]; then + local script_file_path="$9" + + sudo_log 2 "$script_file_label will be passed to the script shell as a path to a file" + + #replace "$PREFIX/" or "~/" prefix with termux absolute paths in script_file_path + sudo_expand_termux_path "script_file_path" "$script_file_label" "$script_file_path" "$TERMUX_PREFIX" "$TERMUX_HOME" 1 + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to expand $script_file_label \"$script_file_path\"" + return $return_value + fi + + #if script_file_path is not a valid absolute path + if [[ ! "$script_file_path" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The $script_file_label \"$script_file_path\" is not a valid absolute path" + return 1 + fi + + sudo_log 2 "script_file_path=\"$script_file_path\"" + printf -v "script_file_path_string" "%q" "$script_file_path" + SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL="$script_file_path_string" + + else + local script_content="$9" + + #if shell is in SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT (dash, sh, node, php) + #or force_use_temp_script_file is enabled + #store the script in a temp script file in SUDO_TEMP_DIRECTORY and pass that as argument to the script shell + if [[ " $SUDO_SCRIPT_SHELLS_WITHOUT_PROCESS_SUBSTITUTION_SUPPORT " == *" $shell_basename "* ]] || [[ "$force_use_temp_script_file" == "1" ]]; then + local log_content=1 + + #if disable_arguments_logging is enabled + if [[ "$disable_arguments_logging" == "1" ]]; then + log_content=0 + fi + + sudo_log 2 "$script_file_label will be passed to the script shell after being store in a temp file" + + #if SUDO_TEMP_DIRECTORY is not a valid absolute path + if [[ ! "$SUDO_TEMP_DIRECTORY" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_TEMP_DIRECTORY \"$SUDO_TEMP_DIRECTORY\" is not a valid absolute path, required by \"sudo_set_script_argument_for_script_shell\"" + return 1 + fi + + script_file="$SUDO_TEMP_DIRECTORY/$script_file_name" + + sudo_log 2 "$script_file_label temp file=\"$script_file\"" + + #sudo_create_sudo_temp_file label path log_content decode_content content + sudo_create_sudo_temp_file "$script_file_label" "$script_file" "$log_content" "$decode_script_content" "$script_content" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_create_sudo_temp_file\"" + return $return_value + fi + + printf -v "script_file_string" "%q" "$script_file" + SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL="$script_file_string" + + #if shell is bash, zsh, fish, ksh, python python2, ruby, perl, lua5.2, lua5.3, lua5.4 + #use process substitution with printf to pass script_content to the script shell + #heredoc is not used since that will create a temp file in TMPDIR which is slower and less secure + #note that ARG_MAX will not cross because of passing the script_content as an argument to printf + #since printf is a shell built-in and exec is not done. It will however cross if an external call were to + #be made to printf with `$PREFIX/bin/print`. The ARG_MAX would be a concern only when `core_script` + #is passed via process substitution to the sudo script and not as a direct argument since in that + #case ARG_MAX would have crossed at the point instead + elif [[ " bash zsh fish ksh python python2 ruby perl lua5.2 lua5.3 lua5.4 " == *" $shell_basename "* ]]; then + + sudo_log 2 "$script_file_label will be passed to the script shell using process substitution with printf command" + + SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL='<(printf "%s" '"'${script_content//\'/\'\\\'\'} +'"')' + + #sudo_log 2 "$script_file_label will be passed to the script shell using process substitution with a cat heredoc" + +# SCRIPT_ARGUMENT_FOR_SCRIPT_SHELL='<(cat <<'"$heredoc_word_quote$heredoc_word$heredoc_word_quote"' +#'"$script_content"' +# +#'"$heredoc_word"' +#)' + + + else + sudo_log_errors "shell_basename \"$shell_basename\" not handled while running \"sudo_set_script_argument_for_script_shell\"" + return 1 + fi + fi + + return 0 + +} + +#sudo_create_sudo_temp_file label path log_content decode_content content +sudo_create_sudo_temp_file() { + + local return_value + + #if parameter count is not 5 + if [ $# -ne 5 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_create_sudo_temp_file\"" + return 1 + fi + + local label="$1" + local path="$2" + local log_content="$3" + local decode_content="$4" + local content="$5" + + #if path is not a valid absolute path + if [[ ! "$path" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The \"$label\" file at path \"$path\" is not an absolute path" + return 2 + fi + + local temp_file_fd + local temp_file_fd_path + + sudo_get_unsed_file_descriptor "temp_file_fd" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$temp_file_fd" =~ $valid_number_regex ]]; then + sudo_log_errors "Failure while getting an unused file descriptor to write decoded $label" + sudo_log_errors "temp_file_fd = \"$temp_file_fd\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + #android fd directory is /proc/self/fd + #using $$ instead of self so that su can access it + temp_file_fd_path="/proc/$$/fd/$temp_file_fd" + + #write the contents to temp_file_fd_path + #decode first if required + #decoding of binary data needs to be done on the fly since + #binary data cannot be stored in a variable to be passed using herestring + #and nor can a heredoc be used to pass binary data + #process substitution is not possible for passing data to su due to /proc/self/fd issues + #hence a fd is opened and used instead + #a herestring or heredoc could have been used for passing text only data + #but those will create a temp file in TMPDIR which is slower and less secure + sudo_log 2 "Writing $label to temp_file_fd \"$temp_file_fd\"" + if [[ "$decode_content" == "1" ]]; then + eval "exec $temp_file_fd<" <(printf '%s' "$content" | base64 -d) + else + eval "exec $temp_file_fd<" <(printf '%s' "$content") + fi + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while writing $label to temp_file_fd \"$temp_file_fd\"" + else + #use cat to read contents from fd at temp_file_fd_path + #and redirect stdout to file at path to write to it + sudo_log 2 "Creating $label file at \"$path\"" + sudo_unset_pre_su_variables + $SU_ENV_COMMAND "cat \"$temp_file_fd_path\" > '${path//\'/\'\\\'\'}'" + return_value=$? + sudo_set_post_su_variables + fi + + #if temp_file_fd is set, then close it + if [ ! -z "$temp_file_fd" ]; then + exec {temp_file_fd}>&- + fi + + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while creating $label file at \"$path\"" + return $return_value + fi + + #read file at path again and log its content but only if binary data is not stored in it + if [[ $sudo_verbose_level -ge 1 ]] && [[ "$log_content" == "1" ]] && [[ "$decode_content" != "1" ]]; then + local file_contents="" + sudo_unset_pre_su_variables + file_contents="$($SU_ENV_COMMAND "cat '${path//\'/\'\\\'\'}'" < /dev/null)" + sudo_set_post_su_variables + sudo_log 1 $'\n'"${label}_CONTENTS=\`$file_contents\`" + fi + + return 0 + +} + +sudo_set_sudo_shell() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_shell" + + #if SUDO_SHELL is set + if [ ! -z "$SUDO_SHELL" ]; then + sudo_trim_trailing_newlines "SUDO_SHELL" "$SUDO_SHELL" + + #validate the SUDO_SHELL + #if use_root_for_path_search_and_validation is enabled + if [[ "$use_root_for_path_search_and_validation" == "1" ]]; then + sudo_unset_pre_su_variables + SUDO_SHELL="$($SU_ENV_COMMAND "source '${SUDO_SCRIPT_PATH//\'/\'\\\'\'}'; sudo_validate_shell \"SUDO_SHELL\" '${SUDO_SHELL//\'/\'\\\'\'}' '${SUDO_SUPPORTED_SHELLS//\'/\'\\\'\'}' \"$TERMUX_PREFIX\" \"$TERMUX_BIN\" \"$TERMUX_HOME\"" < /dev/null)" + return_value=$? + sudo_set_post_su_variables + else + SUDO_SHELL="$(sudo_validate_shell "SUDO_SHELL" "$SUDO_SHELL" "$SUDO_SUPPORTED_SHELLS" "$TERMUX_PREFIX" "$TERMUX_BIN" "$TERMUX_HOME")" + return_value=$? + fi + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to validate \"SUDO_SHELL\"" + return $return_value + fi + else + #default to bash as SUDO_SHELL + SUDO_SHELL="$BASH_SHELL_PATH" + fi + + #if SUDO_SHELL is not a valid absolute path + if [[ ! "$SUDO_SHELL" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_SHELL \"$SUDO_SHELL\" is not a valid absolute path" + return 1 + fi + + sudo_log 1 "SUDO_SHELL=\"$SUDO_SHELL\"" + + #find the basename and parent directory of the SUDO_SHELL + SUDO_SHELL_BASENAME="${SUDO_SHELL##*/}" #strip longest match of */ from start + SUDO_SHELL_PARENT_DIR="${SUDO_SHELL:0:${#SUDO_SHELL} - ${#SUDO_SHELL_BASENAME}}" #substring from 0 to position of basename + case $SUDO_SHELL_PARENT_DIR in *[!/]*/) SUDO_SHELL_PARENT_DIR=${SUDO_SHELL_PARENT_DIR%"${SUDO_SHELL_PARENT_DIR##*[!/]}"};; *[/]) SUDO_SHELL_PARENT_DIR="/";; esac #remove trailing slashes if not root + + sudo_log 2 "SUDO_SHELL_BASENAME=\"$SUDO_SHELL_BASENAME\"" + sudo_log 2 "SUDO_SHELL_PARENT_DIR=\"$SUDO_SHELL_PARENT_DIR\"" + + #if SUDO_SHELL is not in any of the SUDO_SUPPORTED_SHELLS, then exit with error + #the basename of the shell file could be faked but the actual shell inside the file is not checked + if [[ "$SUDO_SHELL_BASENAME" == *' '* ]] || [[ " $SUDO_SUPPORTED_SHELLS " != *" $SUDO_SHELL_BASENAME "* ]]; then + sudo_log_errors "The SUDO_SHELL \"$SUDO_SHELL\" is not supported. It must be one of \"$SUDO_SUPPORTED_SHELLS\"" + return 1 + fi + + return 0 + +} + +sudo_set_sudo_post_shell() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_post_shell" + + #if SUDO_POST_SHELL is set + if [ ! -z "$SUDO_POST_SHELL" ]; then + sudo_trim_trailing_newlines "SUDO_POST_SHELL" "$SUDO_POST_SHELL" + + #validate the SUDO_POST_SHELL + #if use_root_for_path_search_and_validation is enabled + if [[ "$use_root_for_path_search_and_validation" == "1" ]]; then + sudo_unset_pre_su_variables + SUDO_POST_SHELL="$($SU_ENV_COMMAND "source '${SUDO_SCRIPT_PATH//\'/\'\\\'\'}'; sudo_validate_shell \"SUDO_POST_SHELL\" '${SUDO_POST_SHELL//\'/\'\\\'\'}' '${SUDO_SUPPORTED_POST_SHELLS//\'/\'\\\'\'}' \"$TERMUX_PREFIX\" \"$TERMUX_BIN\" \"$TERMUX_HOME\"" < /dev/null)" + return_value=$? + sudo_set_post_su_variables + else + SUDO_POST_SHELL="$(sudo_validate_shell "SUDO_POST_SHELL" "$SUDO_POST_SHELL" "$SUDO_SUPPORTED_POST_SHELLS" "$TERMUX_PREFIX" "$TERMUX_BIN" "$TERMUX_HOME")" + return_value=$? + fi + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to validate \"SUDO_POST_SHELL\"" + return $return_value + fi + else + #default to bash as SUDO_POST_SHELL + SUDO_POST_SHELL="$BASH_SHELL_PATH" + fi + + #if SUDO_POST_SHELL is not a valid absolute path + if [[ ! "$SUDO_POST_SHELL" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_POST_SHELL \"$SUDO_POST_SHELL\" is not a valid absolute path" + return 1 + fi + + sudo_log 1 "SUDO_POST_SHELL=\"$SUDO_POST_SHELL\"" + + #find the basename and parent directory of the SUDO_POST_SHELL + SUDO_POST_SHELL_BASENAME="${SUDO_POST_SHELL##*/}" #strip longest match of */ from start + SUDO_POST_SHELL_PARENT_DIR="${SUDO_POST_SHELL:0:${#SUDO_POST_SHELL} - ${#SUDO_POST_SHELL_BASENAME}}" #substring from 0 to position of basename + case $SUDO_POST_SHELL_PARENT_DIR in *[!/]*/) SUDO_POST_SHELL_PARENT_DIR=${SUDO_POST_SHELL_PARENT_DIR%"${SUDO_POST_SHELL_PARENT_DIR##*[!/]}"};; *[/]) SUDO_POST_SHELL_PARENT_DIR="/";; esac #remove trailing slashes if not root + + sudo_log 2 "SUDO_POST_SHELL_BASENAME=\"$SUDO_POST_SHELL_BASENAME\"" + sudo_log 2 "SUDO_POST_SHELL_PARENT_DIR=\"$SUDO_POST_SHELL_PARENT_DIR\"" + + #if SUDO_POST_SHELL is not in any of the SUDO_SUPPORTED_SHELLS, then exit with error + #the basename of the shell file could be faked but the actual shell inside the file is not checked + if [[ "$SUDO_POST_SHELL_BASENAME" == *' '* ]] || [[ " $SUDO_SUPPORTED_POST_SHELLS " != *" $SUDO_POST_SHELL_BASENAME "* ]]; then + sudo_log_errors "The SUDO_POST_SHELL \"$SUDO_POST_SHELL\" is not supported. It must be one of \"$SUDO_SUPPORTED_POST_SHELLS\"" + return 1 + fi + + return 0 + +} + +#sudo_validate_shell shell_label user_shell supported_shells termux_prefix termux_bin termux_home +sudo_validate_shell() { + + local return_value + + #if parameter count is not 6 + if [ $# -ne 6 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_validate_shell\"" + return 1 + fi + + local shell_label="$1" + local user_shell="$2" + local supported_shells="$3" + local termux_prefix="$4" + local termux_bin="$5" + local termux_home="$6" + + local shell + + #if user_shell is set to a shell in supported_shells, then set user_shell to "$termux_bin/$user_shell" + if [[ "$user_shell" != *'/'* ]]; then + if [[ "$user_shell" != *' '* ]] && [[ " $supported_shells " == *" $user_shell "* ]]; then + shell="$termux_bin/$user_shell" + else + sudo_log_errors "The $shell_label \"$user_shell\" is not supported. It must be one of \"$supported_shells\"" + return 1 + fi + else + #replace "$PREFIX/" or "~/" prefix with termux absolute paths in user_shell + sudo_expand_termux_path "user_shell" "$shell_label" "$user_shell" "$termux_prefix" "$termux_home" 1 + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to expand $shell_label \"$user_shell\"" + return $return_value + fi + + shell="$user_shell" + fi + + #sudo_run_file_type_tests_on_path label path log_file_tests_failure_errors show_stat_output_on_file_tests_failure check_if_absolute_path file_type_tests + sudo_run_file_type_tests_on_path "$shell_label" "$shell" 1 1 1 "frx" || return $? + + #echo shell to stdout + echo "$shell" + +} + +sudo_set_sudo_shell_rcfile() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_shell_rcfile" + + #set rcfile variables + sudo_set_shell_rcfile "SUDO_SHELL" "$SUDO_SHELL_BASENAME" "$SUDO_SHELL_HOME" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_set_rcfile_functions\" for SUDO_SHELL \"$SUDO_SHELL\"" + return $return_value + fi + + SUDO_SHELL_RCFILE="$SHELL_RCFILE" + SUDO_SHELL_RCFILE_PARENT_DIR="$SHELL_RCFILE_PARENT_DIR" + SUDO_SHELL_RCFILE_COMMANDS="$SHELL_RCFILE_COMMANDS" + SUDO_SHELL_RCFILE_VALUE="$SHELL_RCFILE_VALUE" + SUDO_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + + sudo_log 2 "SUDO_SHELL_RCFILE=\"$SUDO_SHELL_RCFILE\"" + sudo_log 2 "SUDO_SHELL_RCFILE_PARENT_DIR=\"$SUDO_SHELL_RCFILE_PARENT_DIR\"" + sudo_log 2 "SUDO_SHELL_RCFILE_COMMANDS=\"$SUDO_SHELL_RCFILE_COMMANDS\"" + sudo_log 2 "SUDO_SHELL_RCFILE_VALUE=\`$SUDO_SHELL_RCFILE_VALUE\`" + + return 0 + +} + +sudo_set_sudo_post_shell_rcfile() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_post_shell_rcfile" + + #set rcfile variables + sudo_set_shell_rcfile "SUDO_POST_SHELL" "$SUDO_POST_SHELL_BASENAME" "$SUDO_POST_SHELL_HOME" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_set_rcfile_functions\" for SUDO_POST_SHELL \"$SUDO_POST_SHELL\"" + return $return_value + fi + + SUDO_POST_SHELL_RCFILE="$SHELL_RCFILE" + SUDO_POST_SHELL_RCFILE_PARENT_DIR="$SHELL_RCFILE_PARENT_DIR" + SUDO_POST_SHELL_RCFILE_COMMANDS="$SHELL_RCFILE_COMMANDS" + SUDO_POST_SHELL_RCFILE_VALUE="$SHELL_RCFILE_VALUE" + SUDO_POST_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + + sudo_log 2 "SUDO_POST_SHELL_RCFILE=\"$SUDO_POST_SHELL_RCFILE\"" + sudo_log 2 "SUDO_POST_SHELL_RCFILE_PARENT_DIR=\"$SUDO_POST_SHELL_RCFILE_PARENT_DIR\"" + sudo_log 2 "SUDO_POST_SHELL_RCFILE_COMMANDS=\"$SUDO_POST_SHELL_RCFILE_COMMANDS\"" + sudo_log 2 "SUDO_POST_SHELL_RCFILE_VALUE=\`$SUDO_POST_SHELL_RCFILE_VALUE\`" + + return 0 + +} + +#sudo_set_shell_rcfile shell_basename shell_home +sudo_set_shell_rcfile() { + + local return_value + + #if parameter count is not 3 + if [ $# -ne 3 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_set_shell_rcfile\"" + return 1 + fi + + local shell_label="$1" + local shell_basename="$2" + local shell_home="$3" + + #RCFILE are usually unique for different shells + #if shell_home==TERMUX_HOME and shell has no --rc param or + #environmental variable, then termux shells and sudo shells will + #have to share rc files + + #was going to add support for tcsh but decided not to after reading the following and having a chuckle + #https://web.fe.up.pt/~jmcruz/etc/unix/sh-vs-csh.html + + SHELL_RCFILE="" + SHELL_RCFILE_PARENT_DIR="" + SHELL_RCFILE_COMMANDS="" + SHELL_RCFILE_VALUE="" + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=() + + #if shell is zsh + if [[ "$shell_basename" == "zsh" ]]; then + #if shell_home==TERMUX_HOME, then termux shell and sudo shell rc files are shared + #since only the parent dir of ".zshrc" file can be defined in ZDOTDIR + SHELL_RCFILE="$shell_home/.zshrc" + SHELL_RCFILE_COMMANDS="export ZDOTDIR='${shell_home//\'/\'\\\'\'}';" + + #if shell is fish + elif [[ "$shell_basename" == "fish" ]]; then + #if shell_home==TERMUX_HOME, then termux shell and sudo shell rc files are shared since "config.fish" is always loaded + #XDG_RUNTIME_DIR is exported and is inside shell_home because otherwise fish shell creates + #the "$TMPDIR/fish.root" directory with root ownership + SHELL_RCFILE="$shell_home/.config/fish/config.fish" + SHELL_RCFILE_COMMANDS="export XDG_CONFIG_HOME='${shell_home//\'/\'\\\'\'}/.config';" + SHELL_RCFILE_COMMANDS+=$'\n'"export XDG_CACHE_HOME='${shell_home//\'/\'\\\'\'}/.cache';" + SHELL_RCFILE_COMMANDS+=$'\n'"export XDG_DATA_HOME='${shell_home//\'/\'\\\'\'}/.local/share';" + SHELL_RCFILE_COMMANDS+=$'\n'"export XDG_RUNTIME_DIR='${shell_home//\'/\'\\\'\'}/.cache/tmp';" + SHELL_RCFILE_COMMANDS+=$'\n'"[ ! -d \"\$XDG_CONFIG_HOME\" ] && mkdir -p \"\$XDG_CONFIG_HOME\";" + SHELL_RCFILE_COMMANDS+=$'\n'"[ ! -d \"\$XDG_DATA_HOME\" ] && mkdir -p \"\$XDG_DATA_HOME\";" + SHELL_RCFILE_COMMANDS+=$'\n'"[ ! -d \"\$XDG_RUNTIME_DIR\" ] && mkdir -p \"\$XDG_RUNTIME_DIR\";" + + #if shell is ruby + elif [[ "$shell_basename" == "ruby" ]]; then + #if shell_home==TERMUX_HOME, then termux shell and sudo shell rc files are shared since "~/.irbrc" is always loaded + SHELL_RCFILE="$shell_home/.irbrc" + + #if shell is pry + elif [[ "$shell_basename" == "pry" ]]; then + #if shell_home==TERMUX_HOME, then termux shell and sudo shell rc files are shared since "~/.pryrc" is always loaded + SHELL_RCFILE="$shell_home/.pryrc" + + #if shell is bash, dash, sh, ksh, php, python + elif [[ " bash dash sh ksh python php python2 " == *" $shell_basename "* ]]; then + #if shell_home==TERMUX_HOME, then set the RCFILE with "sudo_" appended to it + #so that termux shell and sudo shell rc files are not shared since --rc params or + #environmental variables can be used to define the path to the rc file + local shell_rcfile_subname + if [[ "$shell_home" == "$TERMUX_HOME" ]]; then + shell_rcfile_subname="sudo_${shell_basename}" + else + shell_rcfile_subname="${shell_basename}" + fi + + #if shell is bash + if [[ "$shell_basename" == "bash" ]]; then + #use the --rcfile param to pass the path to rcfile + SHELL_RCFILE="$shell_home/.${shell_rcfile_subname}rc" + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("--rcfile" "$SHELL_RCFILE") + + #if shell is python, python2 + elif [[ " python python2 " == *" $shell_basename "* ]]; then + #use the PYTHONSTARTUP environmental variable to pass the path to rcfile + SHELL_RCFILE="$shell_home/.${shell_rcfile_subname}rc" + SHELL_RCFILE_COMMANDS="export PYTHONSTARTUP='${SHELL_RCFILE//\'/\'\\\'\'}';" + + #if shell is php + elif [[ "$shell_basename" == "php" ]]; then + #use the -c param to pass the path to ini file + SHELL_RCFILE="$shell_home/${shell_rcfile_subname}.ini" + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("-c" "$SHELL_RCFILE") + + #if shell is dash, sh, ksh + else + #use the PYTHONSTARTUP environmental variable to pass the path to rcfile + SHELL_RCFILE="$shell_home/.${shell_rcfile_subname}rc" + SHELL_RCFILE_COMMANDS="export ENV='${SHELL_RCFILE//\'/\'\\\'\'}';" + fi + + #if shell is node, perl, lua5.2, lua5.3, lua5.4 + elif [[ " node perl lua5.2 lua5.3 lua5.4 " == *" $shell_basename "* ]]; then + #the shell does not have a rc file or does not use it + sudo_log 2 "The \"$shell_basename\" shell does not have a rc file or does not use it" + else + sudo_log_errors "shell_basename \"$shell_basename\" not handled while running \"sudo_set_shell_rcfile\"" + return 1 + fi + + + #if SHELL_RCFILE is set + if [ ! -z "$SHELL_RCFILE" ]; then + + #if SHELL_RCFILE is not a valid absolute path + if [[ ! "$SHELL_RCFILE" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The ${shell_label}_RCFILE \"$SHELL_RCFILE\" is not a valid absolute path" + return 1 + fi + + #if SHELL_RCFILE_COMMANDS is set + if [ ! -z "$SHELL_RCFILE_COMMANDS" ]; then + SHELL_RCFILE_COMMANDS+=$'\n' + fi + + SHELL_RCFILE_COMMANDS+="export RCFILE='${SHELL_RCFILE//\'/\'\\\'\'}';" + + + #find the parent directory of the SHELL_RCFILE + SHELL_RCFILE_BASENAME="${SHELL_RCFILE##*/}" #strip longest match of */ from start + SHELL_RCFILE_PARENT_DIR="${SHELL_RCFILE:0:${#SHELL_RCFILE} - ${#SHELL_RCFILE_BASENAME}}" #substring from 0 to position of basename + case $SHELL_RCFILE_PARENT_DIR in *[!/]*/) SHELL_RCFILE_PARENT_DIR=${SHELL_RCFILE_PARENT_DIR%"${SHELL_RCFILE_PARENT_DIR##*[!/]}"};; *[/]) SHELL_RCFILE_PARENT_DIR="/";; esac #remove trailing slashes if not root + + #set SHELL_RCFILE_VALUE to be set in SHELL_RCFILE + SHELL_RCFILE_VALUE="" + + #if shell is bash, zsh, dash, sh, fish or ksh + if [[ " bash zsh dash sh fish ksh " == *" $shell_basename "* ]]; then + #not defining any variables since they are dynamically defined by "sudo" command + #overriding may create problems in the future as well if dynamically defined variable values change + #PATH, LD_LIBRARY_PATH and PS1 must never be overridden in rcfile, otherwise "sudo" commands will not work properly, + #specially the "sudo asu" and "sudo <command>" commands + + #define any required functions to add to SHELL_RCFILE_VALUE in RCFILE_FUNCTIONS + sudo_set_rcfile_functions "$shell_basename" || return $? + + #if RCFILE_FUNCTIONS is not empty, then append it to SHELL_RCFILE_VALUE + if [ ! -z "$RCFILE_FUNCTIONS" ]; then + SHELL_RCFILE_VALUE+="$RCFILE_FUNCTIONS"$'\n' + fi + fi + fi + + return 0 + +} + +#sudo_set_rcfile_functions +sudo_set_rcfile_functions() { + + local return_value + + #if parameter count is not 1 + if [ $# -ne 1 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_set_rcfile_functions\"" + return 1 + fi + + local shell_basename="$1" + + RCFILE_FUNCTIONS="" + + #if shell is fish + if [[ "$shell_basename" == "fish" ]]; then + + #define export helper function for fish shell that allows export command in bash to be used + FISH_SHELL_EXPORT_FUNCTION=' +function export + if [ $argv ] + set var (echo $argv | cut -f1 -d=) + set val (echo $argv | cut -f2 -d=) + set -g -x $var $val + end +end + +funcsave export +' + + #define unset helper function for fish shell that allows export command in bash to be used + FISH_SHELL_UNSET_FUNCTION=' +function unset + set --erase $argv +end + +funcsave unset +' + + #define tpath helper function for fish shell that sets priority to termux paths + FISH_SHELL_TPATH_FUNCTION=' +function tpath + export PATH="$TERMUX_PRIORITY_PATH"; + export LD_LIBRARY_PATH="$TERMUX_PRIORITY_LD_LIBRARY_PATH"; + '"$TERMUX_PRIORITY_LD_PRELOAD_COMMAND"' +end +' + + #define apath helper function for fish shell that sets priority to android paths + FISH_SHELL_APATH_FUNCTION=' +function apath + export PATH="$ANDROID_PRIORITY_PATH"; + export LD_LIBRARY_PATH="$ANDROID_PRIORITY_LD_LIBRARY_PATH"; + '"$ANDROID_PRIORITY_LD_PRELOAD_COMMAND"' +end +' + + RCFILE_FUNCTIONS="$FISH_SHELL_EXPORT_FUNCTION" + RCFILE_FUNCTIONS+=$'\n'"$FISH_SHELL_UNSET_FUNCTION" + RCFILE_FUNCTIONS+=$'\n'"$FISH_SHELL_TPATH_FUNCTION" + RCFILE_FUNCTIONS+=$'\n'"$FISH_SHELL_APATH_FUNCTION" + + + + + + #if shell is bash, zsh, dash, sh or ksh + elif [[ " bash zsh dash sh ksh " == *" $shell_basename "* ]]; then + + #define tpath helper function for bash, zsh, sh, etc shells that sets priority to termux paths + GENERAL_SHELL_TPATH_FUNCTION=' +tpath() { + export PATH="$TERMUX_PRIORITY_PATH"; + export LD_LIBRARY_PATH="$TERMUX_PRIORITY_LD_LIBRARY_PATH"; + '"$TERMUX_PRIORITY_LD_PRELOAD_COMMAND"' +} +' + + + #define apath helper function for bash, zsh, sh, etc shells that sets priority to android paths + GENERAL_SHELL_APATH_FUNCTION=' +apath() { + export PATH="$ANDROID_PRIORITY_PATH"; + export LD_LIBRARY_PATH="$ANDROID_PRIORITY_LD_LIBRARY_PATH"; + '"$ANDROID_PRIORITY_LD_PRELOAD_COMMAND"' +} +' + + + RCFILE_FUNCTIONS="$GENERAL_SHELL_TPATH_FUNCTION" + RCFILE_FUNCTIONS+=$'\n'"$GENERAL_SHELL_APATH_FUNCTION" + + fi + + return 0 + +} + +sudo_set_sudo_shell_histfile() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_shell_histfile" + + #set histfile variables + sudo_set_shell_histfile "SUDO_SHELL" "$SUDO_SHELL_BASENAME" "$SUDO_SHELL_HOME" "$sudo_shells_history_enabled" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_set_shell_histfile\" for SUDO_SHELL \"$SUDO_SHELL\"" + return $return_value + fi + + SUDO_SHELL_HISTFILE="$SHELL_HISTFILE" + SUDO_SHELL_HISTFILE_PARENT_DIR="$SHELL_HISTFILE_PARENT_DIR" + SUDO_SHELL_HISTFILE_COMMANDS="$SHELL_HISTFILE_COMMANDS" + SUDO_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + sudo_log 2 "SUDO_SHELL_HISTFILE=\"$SUDO_SHELL_HISTFILE\"" + sudo_log 2 "SUDO_SHELL_HISTFILE_PARENT_DIR=\"$SUDO_SHELL_HISTFILE_PARENT_DIR\"" + sudo_log 2 "SUDO_SHELL_HISTFILE_COMMANDS=\"$SUDO_SHELL_HISTFILE_COMMANDS\"" + + return 0 + +} + +sudo_set_sudo_post_shell_histfile() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_post_shell_histfile" + + #set histfile variables + sudo_set_shell_histfile "SUDO_POST_SHELL" "$SUDO_POST_SHELL_BASENAME" "$SUDO_POST_SHELL_HOME" "$sudo_shells_history_enabled" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_set_shell_histfile\" for SUDO_POST_SHELL \"$SUDO_POST_SHELL\"" + return $return_value + fi + + SUDO_POST_SHELL_HISTFILE="$SHELL_HISTFILE" + SUDO_POST_SHELL_HISTFILE_PARENT_DIR="$SHELL_HISTFILE_PARENT_DIR" + SUDO_POST_SHELL_HISTFILE_COMMANDS="$SHELL_HISTFILE_COMMANDS" + SUDO_POST_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + sudo_log 2 "SUDO_POST_SHELL_HISTFILE=\"$SUDO_POST_SHELL_HISTFILE\"" + sudo_log 2 "SUDO_POST_SHELL_HISTFILE_PARENT_DIR=\"$SUDO_POST_SHELL_HISTFILE_PARENT_DIR\"" + sudo_log 2 "SUDO_POST_SHELL_HISTFILE_COMMANDS=\"$SUDO_POST_SHELL_HISTFILE_COMMANDS\"" + + return 0 + +} + +#sudo_set_shell_histfile shell_label shell_basename shell_home shell_history_enabled +sudo_set_shell_histfile() { + + local return_value + + #if parameter count is not 4 + if [ $# -ne 4 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_set_shell_histfile\"" + return 1 + fi + + local shell_label="$1" + local shell_basename="$2" + local shell_home="$3" + local shell_history_enabled="$4" + + SHELL_HISTFILE="" + SHELL_HISTFILE_PARENT_DIR="" + SHELL_HISTFILE_COMMANDS="" + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=() + + #if shell_history_enabled is enabled + if [[ "$shell_history_enabled" == "1" ]]; then + + #history files are usually unique for different shells + #if shell_home==TERMUX_HOME, then termux shells and sudo + #shells will have to share history files if the shell does + #not support HISTFILE variable + + #if shell is fish + if [[ "$shell_basename" == "fish" ]]; then + local histfile_session_id + + #if shell_home and TERMUX_HOME are same, then set the HISTFILE with "sudo_" + #appended to it to keep sudo shells command history separate from termux shells + if [[ "$shell_home" == "$TERMUX_HOME" ]]; then + histfile_session_id="sudo_fish" + else + histfile_session_id="fish" + fi + + #fish uses the variable fish_history to store the session id for the history file + #the history file ends up being "${fish_history}_history" + SHELL_HISTFILE="$shell_home/.local/share/fish/${histfile_session_id}_history" + SHELL_HISTFILE_COMMANDS="export fish_history='$histfile_session_id';" + + #if shell is python + elif [[ "$shell_basename" == "python" ]]; then + #python uses "~/.python_history" file to store history by default + #history feature was introduced in 3.4 + SHELL_HISTFILE="$shell_home/.python_history" + + #if shell is ruby + elif [[ "$shell_basename" == "ruby" ]]; then + #irb uses "~/.irb_history" file to store history by default + #if rvm is used, then its "~/.irb-history" + #there are no params or environmental variables that can be used to set history settings + #history settings can be changed by defining following config options in "~/.irbrc" file + #IRB.conf[:HISTORY_FILE] = "~/.irb_history" + #IRB.conf[:SAVE_HISTORY] = 1000 + SHELL_HISTFILE="$shell_home/.irb_history" + + #if shell is pry + elif [[ "$shell_basename" == "pry" ]]; then + #pry uses "~/.pry_history" file to store history by default + #there are no params or environmental variables that can be used to set history settings + #history settings can be changed by defining following config options in "~/.pryrc" file + #Pry.config.history_file = "~/.pry_history" + #Pry.config.history_load = true + #Pry.config.history_save = true + SHELL_HISTFILE="$shell_home/.pry_history" + + #if shell is php + elif [[ "$shell_basename" == "php" ]]; then + #php uses "~/.php_history" file to store history by default + SHELL_HISTFILE="$shell_home/.php_history" + + #if shell is bash, zsh, dash, sh, ksh, node, perl + elif [[ " bash zsh dash sh ksh node perl " == *" $shell_basename "* ]]; then + #if shell_home and TERMUX_HOME are same, then set the HISTFILE with "sudo_" + #appended to it to keep sudo shells command history separate from termux shells + local shell_histfile_subname + if [[ "$shell_home" == "$TERMUX_HOME" ]]; then + shell_histfile_subname="sudo_${shell_basename}" + else + shell_histfile_subname="${shell_basename}" + fi + + #if shell is bash + if [[ "$shell_basename" == "bash" ]]; then + #bash uses HISTFILE environment variable for history file + SHELL_HISTFILE="$shell_home/.${shell_histfile_subname}_history" + SHELL_HISTFILE_COMMANDS="export HISTSIZE=1000;" + SHELL_HISTFILE_COMMANDS+=$'\n'"export HISTFILESIZE=1000;" + #if shell is zsh + elif [[ "$shell_basename" == "zsh" ]]; then + #zsh uses HISTFILE environment variable for history file + SHELL_HISTFILE="$shell_home/.${shell_histfile_subname}_history" + SHELL_HISTFILE_COMMANDS="export HISTSIZE=1000;" + SHELL_HISTFILE_COMMANDS+=$'\n'"export SAVEHIST=1000;" + + #if shell is node + elif [[ "$shell_basename" == "node" ]]; then + #node uses NODE_REPL_HISTORY to define path for history file + #and NODE_REPL_HISTORY_SIZE for size of history file + SHELL_HISTFILE="$shell_home/.${shell_histfile_subname}_history" + SHELL_HISTFILE_COMMANDS="export NODE_REPL_HISTORY='${SHELL_HISTFILE//\'/\'\\\'\'}';" + SHELL_HISTFILE_COMMANDS+=$'\n'"export NODE_REPL_HISTORY_SIZE=1000;" + + #if shell is perl + elif [[ "$shell_basename" == "perl" ]]; then + #use rlwrap "--history-filename" and "--histsize" params to set history settings + SHELL_HISTFILE="$shell_home/.${shell_histfile_subname}_history" + SUDO_RLWRAP_ADDITIONAL_COMMAND_OPTIONS+=("--history-filename" "$SHELL_HISTFILE" "--histsize" "1000") + + #if shell is dash sh ksh + else + #the shells use HISTFILE environment variable for history file + SHELL_HISTFILE="$shell_home/.${shell_histfile_subname}_history" + fi + + #if shell is python2, lua5.2, lua5.3, lua5.4 + elif [[ " python2 lua5.2 lua5.3 lua5.4 " == *" $shell_basename "* ]]; then + #the shell does not have a history file or does not use it + sudo_log 2 "The \"$shell_basename\" shell does not have a history file or does not use it" + + else + sudo_log_errors "shell_basename \"$shell_basename\" not handled while running \"sudo_set_shell_histfile\"" + return 1 + fi + + + #if SHELL_HISTFILE is set + if [ ! -z "$SHELL_HISTFILE" ]; then + #if SHELL_HISTFILE is not a valid absolute path + if [[ ! "$SHELL_HISTFILE" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The ${shell_label}_HISTFILE \"$SHELL_HISTFILE\" is not a valid absolute path" + return 1 + fi + + #if SHELL_HISTFILE_COMMANDS is set + if [ ! -z "$SHELL_HISTFILE_COMMANDS" ]; then + SHELL_HISTFILE_COMMANDS+=$'\n' + fi + + SHELL_HISTFILE_COMMANDS+="export HISTFILE='${SHELL_HISTFILE//\'/\'\\\'\'}';" + + #find the parent directory of the SHELL_HISTFILE + SHELL_HISTFILE_BASENAME="${SHELL_HISTFILE##*/}" #strip longest match of */ from start + SHELL_HISTFILE_PARENT_DIR="${SHELL_HISTFILE:0:${#SHELL_HISTFILE} - ${#SHELL_HISTFILE_BASENAME}}" #substring from 0 to position of basename + case $SHELL_HISTFILE_PARENT_DIR in *[!/]*/) SHELL_HISTFILE_PARENT_DIR=${SHELL_HISTFILE_PARENT_DIR%"${SHELL_HISTFILE_PARENT_DIR##*[!/]}"};; *[/]) SHELL_HISTFILE_PARENT_DIR="/";; esac #remove trailing slashes if not root + fi + + else + #if shell is fish + if [[ "$shell_basename" == "fish" ]]; then + #fish uses '--private' flag to disable history + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("--private") + + #if shell is python + elif [[ "$shell_basename" == "python" ]]; then + #disable the "readline.write_history_file" function that writes history to file + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("-c" 'import readline; readline.write_history_file = lambda *args: None') + + #if shell is pry + elif [[ "$shell_basename" == "pry" ]]; then + #use the "Pry.config.history_save" config to disable history saving + #the "--no-history" flag to disable history loading is raising an exception in pry-0.13.1 + #using the "Pry.config.history_load" config option with "-e" to disable history loading + #does not work, since its already loaded by the time its run + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("-e" 'Pry.config.history_save = false;') + + #if shell is node + elif [[ "$shell_basename" == "node" ]]; then + #node uses NODE_REPL_HISTORY to define path for history file + #unsetting it disables history + SHELL_HISTFILE_COMMANDS="export NODE_REPL_HISTORY='';" + + #if shell is perl + elif [[ "$shell_basename" == "perl" ]]; then + #use rlwrap "--history-filename" to set history settings + SUDO_RLWRAP_ADDITIONAL_COMMAND_OPTIONS+=("--history-filename" "/dev/null") + + #if shell is bash, zsh, dash, sh, ksh + elif [[ " bash zsh dash sh ksh " == *" $shell_basename "* ]]; then + #if shell is bash + #if [[ "$shell_basename" == "bash" ]]; then + #bash uses 'history' shopt_option to control history, so unset it with a command option + #for some reason this does not work since bash shell automatically enables history by default for interactive shells + #can be confirmed by running "shopt -o" in a new interactive shell + #SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS+=("+o" "history") + #fi + + #unsetting HISTFILE does not work for all shells like bash since it sets the default value if HISTFILE is not set + #HISTFILE may still be overridden in a startup or a rc file + SHELL_HISTFILE_COMMANDS="export HISTFILE='/dev/null';" + + #if shell is ruby, lua5.2, lua5.3, lua5.4, php, python2 + elif [[ " ruby lua5.2 lua5.3 lua5.4 php python2 " == *" $shell_basename "* ]]; then + #the shell does not have a history file or does not use it or cannot be disabled using any params or environmental variables + sudo_log 2 "The \"$shell_basename\" shell does not have a history file or does not use it or cannot be disabled using any params or environmental variables" + + else + sudo_log_errors "shell_basename \"$shell_basename\" not handled while running \"sudo_set_shell_histfile\"" + return 1 + fi + fi + + return 0 + +} + +sudo_set_sudo_shell_command() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_shell_command" + + SHELL_ADDITIONAL_COMMAND_OPTIONS=("${SUDO_SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=("${SUDO_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + #set shell command variables + sudo_set_shell_command "SUDO_SHELL" "$SUDO_SHELL" "$SUDO_SHELL_BASENAME" "$SUDO_SHELL_PARENT_DIR" "$interactive_sudo_shell_required" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_set_shell_command\" for SUDO_SHELL \"$SUDO_SHELL\"" + return $return_value + fi + + SUDO_SHELL_COMMAND=("${SHELL_COMMAND[@]}") + SUDO_SHELL_INTERACTIVE_COMMAND=("${SHELL_INTERACTIVE_COMMAND[@]}") + SUDO_SHELL_COMMAND_PRINT_FORMAT="$SHELL_COMMAND_PRINT_FORMAT" + SUDO_SHELL_INTERACTIVE_COMMAND_PRINT_FORMAT="$SHELL_INTERACTIVE_COMMAND_PRINT_FORMAT" + + + sudo_log 2 "SUDO_SHELL_COMMAND=\"${SUDO_SHELL_COMMAND[*]}\"" + sudo_log 2 "SUDO_SHELL_INTERACTIVE_COMMAND=\"${SUDO_SHELL_INTERACTIVE_COMMAND[*]}\"" + + return 0 + +} + +sudo_set_sudo_post_shell_command() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_post_shell_command" + + SHELL_ADDITIONAL_COMMAND_OPTIONS=() + SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=("${SUDO_POST_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + #set shell command variables + sudo_set_shell_command "SUDO_POST_SHELL" "$SUDO_POST_SHELL" "$SUDO_POST_SHELL_BASENAME" "$SUDO_POST_SHELL_PARENT_DIR" 1 + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_set_shell_command\" for SUDO_POST_SHELL \"$SUDO_POST_SHELL\"" + return $return_value + fi + + SUDO_POST_SHELL_INTERACTIVE_COMMAND=("${SHELL_INTERACTIVE_COMMAND[@]}") + SUDO_POST_SHELL_INTERACTIVE_COMMAND_PRINT_FORMAT="$SHELL_INTERACTIVE_COMMAND_PRINT_FORMAT" + + sudo_log 2 "SUDO_POST_SHELL_INTERACTIVE_COMMAND=\"${SUDO_POST_SHELL_INTERACTIVE_COMMAND[*]}\"" + + return 0 + +} + +#sudo_set_shell_command shell_label shell shell_basename shell_parent_dir interactive_shell_required +sudo_set_shell_command() { + + local return_value + + #if parameter count is not 5 + if [ $# -ne 5 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_set_shell_command\"" + return 1 + fi + + local shell_label="$1" + local shell="$2" + local shell_basename="$3" + local shell_parent_dir="$4" + local interactive_shell_required="$5" + + local shell_dependency_label="" + local shell_dependency="" + local shell_interactive_dependency_label="" + local shell_interactive_dependency="" + + SHELL_COMMAND_PRINT_FORMAT="%q " + SHELL_INTERACTIVE_COMMAND_PRINT_FORMAT="%q " + + #if shell is bash + if [[ "$shell_basename" == "bash" ]]; then + SHELL_COMMAND=("$shell" "--noprofile" "--norc" "${SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + SHELL_INTERACTIVE_COMMAND=("$shell" "${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}" "-i") + + #if shell is zsh + elif [[ "$shell_basename" == "zsh" ]]; then + SHELL_COMMAND=("$shell" "--no-rcs" "${SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + SHELL_INTERACTIVE_COMMAND=("$shell" "${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}" "-i") + + #if shell is python, python2 + elif [[ " python python2 " == *" $shell_basename "* ]]; then + SHELL_COMMAND=("$shell" "${SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + SHELL_INTERACTIVE_COMMAND=("$shell" "-i" "${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + #if shell is ruby + elif [[ "$shell_basename" == "ruby" ]]; then + #the ruby interactive shell is named irb + SHELL_COMMAND=("$shell" "${SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + SHELL_INTERACTIVE_COMMAND=("$shell_parent_dir/irb" "${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + shell_interactive_dependency_label="irb" + shell_interactive_dependency="$TERMUX_BIN/irb" + + #if shell is pry + elif [[ "$shell_basename" == "pry" ]]; then + #pry does not support passing scripts, ruby must be used instead + SHELL_COMMAND=() + SHELL_INTERACTIVE_COMMAND=("$shell" "${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}") + + #if shell is perl + elif [[ "$shell_basename" == "perl" ]]; then + SHELL_COMMAND=("$shell" "${SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + + #the perl interactive shell is started with rlwrap + #rlwrap -A -pgreen -S "perl> " perl -wnE 'say eval()//$@' + local shell_interactive_command_string; + printf -v "shell_interactive_command_string" "%q " \ + "$TERMUX_BIN/rlwrap" "-A" "-pgreen" "-S" "perl> " "${SUDO_RLWRAP_ADDITIONAL_COMMAND_OPTIONS[@]}" \ + "$shell_parent_dir/perl" "${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}" "-wnE" 'say eval()//$@' + SHELL_INTERACTIVE_COMMAND=("$shell_interactive_command_string") + SHELL_INTERACTIVE_COMMAND_PRINT_FORMAT="%s" + + shell_interactive_dependency_label="rlwrap" + shell_interactive_dependency="$TERMUX_BIN/rlwrap" + + #if shell is php + elif [[ "$shell_basename" == "php" ]]; then + SHELL_COMMAND=("$shell" "${SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + SHELL_INTERACTIVE_COMMAND=("$shell" "${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}" "-a") + + #if shell is dash, sh, ksh, fish, node, lua5.2, lua5.3, lua5.4 + elif [[ " dash sh ksh fish node lua5.2 lua5.3 lua5.4 " == *" $shell_basename "* ]]; then + SHELL_COMMAND=("$shell" "${SHELL_ADDITIONAL_COMMAND_OPTIONS[@]}") + SHELL_INTERACTIVE_COMMAND=("$shell" "${SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS[@]}" "-i") + else + sudo_log_errors "shell_basename \"$shell_basename\" not handled while running \"sudo_set_shell_command\"" + return 1 + fi + + #if interactive_shell_required is not enabled + if [[ "$interactive_shell_required" != "1" ]]; then + #if shell_dependency is set + if [ ! -z "$shell_dependency" ]; then + #if use_root_for_path_search_and_validation is enabled + if [[ "$use_root_for_path_search_and_validation" == "1" ]]; then + sudo_unset_pre_su_variables + SUDO_POST_SHELL="$($SU_ENV_COMMAND "source '${SUDO_SCRIPT_PATH//\'/\'\\\'\'}'; sudo_run_file_type_tests_on_path \"$shell_dependency_label\" '${shell_dependency//\'/\'\\\'\'}' 1 1 1 \"frx\"" < /dev/null)" + return_value=$? + sudo_set_post_su_variables + else + sudo_run_file_type_tests_on_path "$shell_dependency_label" "$shell_dependency" 1 1 1 "frx" + return_value=$? + fi + if [ $return_value -ne 0 ]; then + sudo_log_errors "\"$shell_dependency_label\" is required to run non-interactive \"$shell_basename\" shell" + return $return_value + fi + fi + else + #if shell_interactive_dependency is set + if [ ! -z "$shell_interactive_dependency" ]; then + #if use_root_for_path_search_and_validation is enabled + if [[ "$use_root_for_path_search_and_validation" == "1" ]]; then + sudo_unset_pre_su_variables + SUDO_POST_SHELL="$($SU_ENV_COMMAND "source '${SUDO_SCRIPT_PATH//\'/\'\\\'\'}'; sudo_run_file_type_tests_on_path \"$shell_interactive_dependency_label\" '${shell_interactive_dependency//\'/\'\\\'\'}' 1 1 1 \"frx\"" < /dev/null)" + return_value=$? + sudo_set_post_su_variables + else + sudo_run_file_type_tests_on_path "$shell_interactive_dependency_label" "$shell_interactive_dependency" 1 1 1 "frx" + return_value=$? + fi + if [ $return_value -ne 0 ]; then + sudo_log_errors "\"$shell_interactive_dependency_label\" is required to run interactive \"$shell_basename\" shell" + return $return_value + fi + fi + fi + + return 0 + +} + +sudo_setup_sudo_shell_home_and_working_environment_wrapper() { + + local return_value + + sudo_log_literal 2 "\n\n\nRunning sudo_setup_sudo_shell_home_and_working_environment_wrapper stage 1" + + SETUP_SUDO_TEMP_DIRECTORY=0 + SUDO_TEMP_DIRECTORY_FD="" + + + #if command_type equals "script" and + if [[ "$command_type" == "script" ]]; then + #enable SETUP_SUDO_TEMP_DIRECTORY if required + sudo_check_if_setting_up_sudo_temp_directory_is_required || return $? + + #if SETUP_SUDO_TEMP_DIRECTORY is enabled + if [[ "$SETUP_SUDO_TEMP_DIRECTORY" == "1" ]]; then + + #find an unused fd that is passed to su which the function sudo_setup_sudo_temp_directory will use to write the SUDO_TEMP_DIRECTORY value + #which is later read after su returns + #stdout cannot be used to return SUDO_TEMP_DIRECTORY from su using a subshell due to logging and possibly other output to stdout + sudo_get_unsed_file_descriptor "SUDO_TEMP_DIRECTORY_FD" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$SUDO_TEMP_DIRECTORY_FD" =~ $valid_number_regex ]]; then + sudo_log_errors "Failure while getting an unused file descriptor to write SUDO_TEMP_DIRECTORY" + sudo_log_errors "SUDO_TEMP_DIRECTORY_FD = \"$SUDO_TEMP_DIRECTORY_FD\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + #android fd directory is /proc/self/fd + #using $$ instead of self so that su can access it + SUDO_TEMP_DIRECTORY_FD_PATH="/proc/$$/fd/$SUDO_TEMP_DIRECTORY_FD" + + sudo_log 2 "SUDO_TEMP_DIRECTORY_FD_PATH=\"$SUDO_TEMP_DIRECTORY_FD_PATH\"" + + #write empty string to fd just to create it + eval "exec $SUDO_TEMP_DIRECTORY_FD<" <(echo -n "") + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while opening SUDO_TEMP_DIRECTORY_FD \"$SUDO_TEMP_DIRECTORY_FD\" to write SUDO_TEMP_DIRECTORY" + return $return_value + fi + fi + fi + + #if SETUP_SUDO_TEMP_DIRECTORY is not enabled and dry_run_sudo is enabled, + #then just return since no need to create homes, rc files, history files, work dir and temp dir + if [[ "$SETUP_SUDO_TEMP_DIRECTORY" != "1" ]] && [[ "$dry_run_sudo" == "1" ]]; then + return 0 + fi + + + + #remount android rootfs "/" partition and/or system "/system" partition as rw if + #they need to be used for SUDO_SHELL_HOME, SUDO_POST_SHELL_HOME or SUDO_SHELL_WORKING_DIR + sudo_remount_partitions_for_sudo_shell_homes "rw" || return $? + + + + local -a SU_ENV_COMMAND_ADDITIONAL_VARIABLES_TO_EXPORT_ARRAY=( + SUDO_SHELL_HOME_PARENT_DIR + SUDO_SHELL_HOME + + sudo_shells_automatically_create_rc_files + SUDO_SHELL_RCFILE_PARENT_DIR + SUDO_SHELL_RCFILE + SUDO_SHELL_RCFILE_VALUE + + sudo_shells_automatically_create_history_files + SUDO_SHELL_HISTFILE_PARENT_DIR + SUDO_SHELL_HISTFILE + + SUDO_POST_SHELL_HOME_PARENT_DIR + SUDO_POST_SHELL_HOME + + sudo_shells_automatically_create_rc_files + SUDO_POST_SHELL_RCFILE_PARENT_DIR + SUDO_POST_SHELL_RCFILE + SUDO_POST_SHELL_RCFILE_VALUE + + sudo_shells_automatically_create_history_files + SUDO_POST_SHELL_HISTFILE_PARENT_DIR + SUDO_POST_SHELL_HISTFILE + + SUDO_SHELL_WORKING_DIR + + remove_previous_sudo_temp_files + SETUP_SUDO_TEMP_DIRECTORY + SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY + SUDO_TEMP_DIRECTORY_PREFIX + SUDO_TEMP_DIRECTORY + SUDO_TEMP_DIRECTORY_FD_PATH + + TERMUX_FILES + TERMUX_PREFIX + TERMUX_PREFIX_BASENAME + TERMUX_HOME + TERMUX_HOME_BASENAME + + dry_run_sudo + ) + + local var + + #export additional variables that are required by su that were not exported by sudo_export_or_unexport_shared_variables_for_su + for var in "${SU_ENV_COMMAND_ADDITIONAL_VARIABLES_TO_EXPORT_ARRAY[@]}"; do + export "${var}" + done + + + + sudo_log_literal 2 "\nRunning sudo_setup_sudo_shell_home_and_working_environment" + + #run sudo_setup_sudo_shell_home_and_working_environment that sets up the sudo environment + sudo_unset_pre_su_variables + $SU_ENV_COMMAND "source '${SUDO_SCRIPT_PATH//\'/\'\\\'\'}'; sudo_setup_sudo_shell_home_and_working_environment" + return_value=$? + sudo_set_post_su_variables + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_setup_sudo_shell_home_and_working_environment\"" + return $return_value + fi + + + + #remove export property from additional variables exported earlier + for var in "${SU_ENV_COMMAND_ADDITIONAL_VARIABLES_TO_EXPORT_ARRAY[@]}"; do + export -n "${var}" + done + + + + + sudo_log_literal 2 "\nRunning sudo_setup_sudo_shell_home_and_working_environment_wrapper stage 2" + + #if SETUP_SUDO_TEMP_DIRECTORY is enabled + if [[ "$SETUP_SUDO_TEMP_DIRECTORY" == "1" ]]; then + #if SUDO_TEMP_DIRECTORY_FD_PATH does not exist + if [ ! -e "$SUDO_TEMP_DIRECTORY_FD_PATH" ]; then + sudo_log_errors "The SUDO_TEMP_DIRECTORY_FD_PATH \"$SUDO_TEMP_DIRECTORY_FD_PATH\" does not exist that needs to be used to read SUDO_TEMP_DIRECTORY" + return 1 + fi + + #read the value of SUDO_TEMP_DIRECTORY from SUDO_TEMP_DIRECTORY_FD_PATH which the sudo_setup_sudo_temp_directory function would have written while running su + SUDO_TEMP_DIRECTORY="$(cat "$SUDO_TEMP_DIRECTORY_FD_PATH")" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure to read SUDO_TEMP_DIRECTORY from SUDO_TEMP_DIRECTORY_FD_PATH \"$SUDO_TEMP_DIRECTORY_FD_PATH\"" + return $return_value + fi + + #if SUDO_TEMP_DIRECTORY is not a valid absolute path + if [[ ! "$SUDO_TEMP_DIRECTORY" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_TEMP_DIRECTORY \"$SUDO_TEMP_DIRECTORY\" is not a valid absolute path" + return 1 + fi + + sudo_log 2 "SUDO_TEMP_DIRECTORY=\"$SUDO_TEMP_DIRECTORY\"" + + #if SUDO_TEMP_DIRECTORY_FD is set, then close it + if [ ! -z "$SUDO_TEMP_DIRECTORY_FD" ]; then + exec {SUDO_TEMP_DIRECTORY_FD}>&- + SUDO_TEMP_DIRECTORY_FD="" + fi + fi + + return 0 + +} + +sudo_setup_sudo_shell_home_and_working_environment() { + + local return_value + + #sudo_setup_shell_home shell_label shell_home_parent_dir shell_home shell_automatically_create_rc_file shell_rcfile_parent_dir shell_rcfile shell_rcfile_value shell_histfile_parent_dir shell_histfile + sudo_setup_shell_home "SUDO_SHELL" "$SUDO_SHELL_HOME_PARENT_DIR" "$SUDO_SHELL_HOME" "$sudo_shells_automatically_create_rc_files" "$SUDO_SHELL_RCFILE_PARENT_DIR" "$SUDO_SHELL_RCFILE" "$SUDO_SHELL_RCFILE_VALUE" "$sudo_shells_automatically_create_history_files" "$SUDO_SHELL_HISTFILE_PARENT_DIR" "$SUDO_SHELL_HISTFILE" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 112 ]; then + sudo_log_errors "Failed to setup SUDO_SHELL_HOME" + return $return_value + fi + + #if dry_run_sudo is not enabled, only then run it + #if it is enabled, then we must be running this function because SETUP_SUDO_TEMP_DIRECTORY was enabled + #in which case no need to create post shell home and work dir + if [[ "$dry_run_sudo" != "1" ]]; then + + #if SUDO_POST_SHELL_HOME is set + if [ ! -z "$SUDO_POST_SHELL_HOME" ]; then + #if SUDO_SHELL and SUDO_POST_SHELL home, rcfile or histfiles are different + if [[ "$SUDO_POST_SHELL_HOME" != "$SUDO_SHELL_HOME" ]] || \ + [[ "$SUDO_POST_SHELL_RCFILE" != "$SUDO_SHELL_RCFILE" ]] || \ + [[ "$SUDO_POST_SHELL_HISTFILE" != "$SUDO_SHELL_HISTFILE" ]]; then + #sudo_setup_shell_home shell_label shell_home_parent_dir shell_home shell_automatically_create_rc_file shell_rcfile_parent_dir shell_rcfile shell_rcfile_value shell_histfile_parent_dir shell_histfile + sudo_setup_shell_home "SUDO_POST_SHELL" "$SUDO_POST_SHELL_HOME_PARENT_DIR" "$SUDO_POST_SHELL_HOME" "$sudo_shells_automatically_create_rc_files" "$SUDO_POST_SHELL_RCFILE_PARENT_DIR" "$SUDO_POST_SHELL_RCFILE" "$SUDO_POST_SHELL_RCFILE_VALUE" "$sudo_shells_automatically_create_history_files" "$SUDO_POST_SHELL_HISTFILE_PARENT_DIR" "$SUDO_POST_SHELL_HISTFILE" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 112 ]; then + sudo_log_errors "Failed to setup SUDO_POST_SHELL_HOME" + return $return_value + fi + fi + fi + + #if SUDO_SHELL_WORKING_DIR is set + if [ ! -z "$SUDO_SHELL_WORKING_DIR" ]; then + #create SUDO_SHELL_WORKING_DIR if missing + sudo_setup_sudo_shell_working_dir || return $? + fi + fi + + #setup SUDO_TEMP_DIRECTORY + sudo_setup_sudo_temp_directory + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_setup_sudo_temp_directory\"" + return $return_value + fi + + return 0 + +} + +#sudo_setup_shell_home shell_label shell_home_parent_dir shell_home shell_automatically_create_rc_file shell_rcfile_parent_dir shell_rcfile shell_rcfile_value shell_histfile_parent_dir shell_histfile +sudo_setup_shell_home() { + + local return_value + + #if parameter count is not 10 + if [ $# -ne 10 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_setup_shell_home\"" + return 1 + fi + + local shell_label="$1" + local shell_home_parent_dir="$2" + local shell_home="$3" + local shell_automatically_create_rc_file="$4" + local shell_rcfile_parent_dir="$5" + local shell_rcfile="$6" + local shell_rcfile_value="$7" + local shell_automatically_create_history_file="$8" + local shell_histfile_parent_dir="$9" + local shell_histfile="${10}" + + local create_shell_home_if_it_does_not_exist + local only_set_perms_and_ownership_on_creation_of_shell_home + local shell_home_ownership + local shell_home_permission + local shell_home_file_type_tests + + local create_shell_rcfile_parent_dir_if_it_does_not_exist + local only_set_perms_and_ownership_on_creation_of_shell_rcfile_parent_dir + local shell_rcfile_parent_dir_ownership + local shell_rcfile_parent_dir_permission + local shell_rcfile_parent_dir_file_type_tests + + local create_shell_rcfile_if_it_does_not_exist + local only_set_perms_and_ownership_on_creation_of_shell_rcfile + local shell_rcfile_ownership + local shell_rcfile_permission + local shell_rcfile_file_type_tests + + local create_shell_histfile_parent_dir_if_it_does_not_exist + local only_set_perms_and_ownership_on_creation_of_shell_histfile_parent_dir + local shell_histfile_parent_dir_ownership + local shell_histfile_parent_dir_permission + local shell_histfile_parent_dir_file_type_tests + + local create_shell_histfile_if_it_does_not_exist + local only_set_perms_and_ownership_on_creation_of_shell_histfile + local shell_histfile_ownership + local shell_histfile_permission + local shell_histfile_file_type_tests + + local validation_result + + sudo_log_literal 2 "\nRunning sudo_setup_shell_home for $shell_label" + + + + create_shell_home_if_it_does_not_exist=1 + only_set_perms_and_ownership_on_creation_of_shell_home=1 + shell_home_permission="700" + shell_home_file_type_tests="rwx" + + #validate if shell_home is a whitelisted path under TERMUX_FILES + sudo_validate_path_is_a_whitelisted_path_under_termux_files "${shell_label}_HOME" "$shell_home" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 2 ] && [ $return_value -ne 3 ]; then + return $return_value + fi + + validation_result=$return_value + + #if shell_home is a whitelisted path under TERMUX_FILES + if [ $validation_result -eq 0 ]; then + #if shell_home and TERMUX_HOME are same, then root ownership + #must not be set to it otherwise termux app non-root shell will not work + if [[ "$shell_home" == "$TERMUX_HOME" ]]; then + shell_home_ownership="--reference=\"$TERMUX_FILES\"" + else + shell_home_ownership="root:root" + fi + #if shell_home is a blacklisted path under TERMUX_FILES + elif [ $validation_result -eq 2 ]; then + sudo_log_errors "The \"$shell_home\" cannot be used as ${shell_label}_HOME" + return 1 + #if shell_home is not under TERMUX_FILES + else + shell_home_ownership="--reference='${shell_home_parent_dir//\'/\'\\\'\'}'" + fi + + + + #if shell_automatically_create_rc_file is not enabled or dry_run_sudo is enabled, then unset shell_rcfile + if [[ "$shell_automatically_create_rc_file" != "1" ]] || [[ "$dry_run_sudo" == "1" ]]; then + shell_rcfile="" + fi + + #if shell_rcfile is set + if [ ! -z "$shell_rcfile" ]; then + #if shell_rcfile_parent_dir is not the same as shell_home or one of subdirectories + #otherwise remounting rootfs and system partition would become too complicated + if [[ "$shell_rcfile_parent_dir" != "$shell_home" ]] && [[ "$shell_rcfile_parent_dir" != "$shell_home"/* ]]; then + sudo_log_errors "The ${shell_label}_RCFILE_PARENT_DIR \"$shell_rcfile_parent_dir\" must be same as ${shell_label}_HOME \"$shell_home\" or be one of its subdirectories" + return 1 + fi + + create_shell_rcfile_parent_dir_if_it_does_not_exist=1 + only_set_perms_and_ownership_on_creation_of_shell_rcfile_parent_dir=1 + shell_rcfile_parent_dir_permission="700" + shell_rcfile_parent_dir_file_type_tests="rwx" + + create_shell_rcfile_if_it_does_not_exist=1 + only_set_perms_and_ownership_on_creation_of_shell_rcfile=1 + shell_rcfile_permission="600" + shell_rcfile_file_type_tests="rw" + + #validate if shell_rcfile_parent_dir is a whitelisted path under TERMUX_FILES + sudo_validate_path_is_a_whitelisted_path_under_termux_files "${shell_label}_RCFILE_PARENT_DIR" "$shell_rcfile_parent_dir" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 2 ] && [ $return_value -ne 3 ]; then + return $return_value + fi + + validation_result=$return_value + + #if shell_rcfile_parent_dir is a whitelisted path under TERMUX_FILES + if [ $validation_result -eq 0 ]; then + #if shell_rcfile_parent_dir and TERMUX_HOME are same, then root ownership + #must not be set to it otherwise termux app non-root shell will not work + if [[ "$shell_rcfile_parent_dir" == "$TERMUX_HOME" ]]; then + shell_rcfile_parent_dir_ownership="--reference=\"$TERMUX_FILES\"" + shell_rcfile_ownership="--reference=\"$TERMUX_FILES\"" + else + shell_rcfile_parent_dir_ownership="root:root" + shell_rcfile_ownership="root:root" + fi + #if shell_rcfile_parent_dir is a blacklisted path under TERMUX_FILES + elif [ $validation_result -eq 2 ]; then + sudo_log_errors "The \"$shell_rcfile_parent_dir\" cannot be used as ${shell_label}_RCFILE_PARENT_DIR" + return 1 + #if shell_rcfile_parent_dir is not under TERMUX_FILES + else + #if shell_rcfile_parent_dir and shell_home are same + if [[ "$shell_rcfile_parent_dir" == "$shell_home" ]]; then + shell_rcfile_parent_dir_ownership="--reference='${shell_home_parent_dir//\'/\'\\\'\'}'" + shell_rcfile_ownership="--reference='${shell_home_parent_dir//\'/\'\\\'\'}'" + else + + #find the parent directory of the shell_rcfile_parent_dir + shell_rcfile_parent_dir_basename="${shell_rcfile_parent_dir##*/}" #strip longest match of */ from start + shell_rcfile_parent_dir_parent_dir="${shell_rcfile_parent_dir:0:${#shell_rcfile_parent_dir} - ${#shell_rcfile_parent_dir_basename}}" #substring from 0 to position of basename + case $shell_rcfile_parent_dir_parent_dir in *[!/]*/) shell_rcfile_parent_dir_parent_dir=${shell_rcfile_parent_dir_parent_dir%"${shell_rcfile_parent_dir_parent_dir##*[!/]}"};; *[/]) shell_rcfile_parent_dir_parent_dir="/";; esac #remove trailing slashes if not root + + shell_rcfile_parent_dir_ownership="--reference='${shell_rcfile_parent_dir_parent_dir//\'/\'\\\'\'}'" + shell_rcfile_ownership="--reference='${shell_rcfile_parent_dir//\'/\'\\\'\'}'" + fi + fi + fi + + + + #if shell_automatically_create_history_files is not enabled or dry_run_sudo is enabled, then unset shell_histfile + if [[ "$shell_automatically_create_history_file" != "1" ]] || [[ "$dry_run_sudo" == "1" ]]; then + shell_histfile="" + fi + + #if shell_histfile is set + if [ ! -z "$shell_histfile" ]; then + + #if shell_histfile_parent_dir is not the same as shell_home or one of subdirectories + #otherwise remounting rootfs and system partition would become too complicated + if [[ "$shell_histfile_parent_dir" != "$shell_home" ]] && [[ "$shell_histfile_parent_dir" != "$shell_home"/* ]]; then + sudo_log_errors "The ${shell_label}_HISTFILE_PARENT_DIR \"$shell_histfile_parent_dir\" must be same as ${shell_label}_HOME \"$shell_home\" or be one of its subdirectories" + return 1 + fi + + create_shell_histfile_parent_dir_if_it_does_not_exist=1 + only_set_perms_and_ownership_on_creation_of_shell_histfile_parent_dir=1 + shell_histfile_parent_dir_permission="700" + shell_histfile_parent_dir_file_type_tests="rwx" + + create_shell_histfile_if_it_does_not_exist=1 + only_set_perms_and_ownership_on_creation_of_shell_histfile=1 + shell_histfile_permission="600" + shell_histfile_file_type_tests="rw" + + #validate if shell_histfile_parent_dir is a whitelisted path under TERMUX_FILES + sudo_validate_path_is_a_whitelisted_path_under_termux_files "${shell_label}_HISTFILE_PARENT_DIR" "$shell_histfile_parent_dir" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 2 ] && [ $return_value -ne 3 ]; then + return $return_value + fi + + validation_result=$return_value + + #if shell_histfile_parent_dir is a whitelisted path under TERMUX_FILES + if [ $validation_result -eq 0 ]; then + #if shell_histfile_parent_dir and TERMUX_HOME are same, then root ownership + #must not be set to it otherwise termux app non-root shell will not work + if [[ "$shell_histfile_parent_dir" == "$TERMUX_HOME" ]]; then + shell_histfile_parent_dir_ownership="--reference=\"$TERMUX_FILES\"" + shell_histfile_ownership="--reference=\"$TERMUX_FILES\"" + else + shell_histfile_parent_dir_ownership="root:root" + shell_histfile_ownership="root:root" + fi + #if shell_histfile_parent_dir is a blacklisted path under TERMUX_FILES + elif [ $validation_result -eq 2 ]; then + sudo_log_errors "The \"$shell_histfile_parent_dir\" cannot be used as ${shell_label}_HISTFILE_PARENT_DIR" + return 1 + #if shell_histfile_parent_dir is not under TERMUX_FILES + else + #if shell_histfile_parent_dir and shell_home are same + if [[ "$shell_histfile_parent_dir" == "$shell_home" ]]; then + shell_histfile_parent_dir_ownership="--reference='${shell_home_parent_dir//\'/\'\\\'\'}'" + shell_histfile_ownership="--reference='${shell_home_parent_dir//\'/\'\\\'\'}'" + else + #find the parent directory of the shell_histfile_parent_dir + shell_histfile_parent_dir_basename="${shell_histfile_parent_dir##*/}" #strip longest match of */ from start + shell_histfile_parent_dir_parent_dir="${shell_histfile_parent_dir:0:${#shell_histfile_parent_dir} - ${#shell_histfile_parent_dir_basename}}" #substring from 0 to position of basename + case $shell_histfile_parent_dir_parent_dir in *[!/]*/) shell_histfile_parent_dir_parent_dir=${shell_histfile_parent_dir_parent_dir%"${shell_histfile_parent_dir_parent_dir##*[!/]}"};; *[/]) shell_histfile_parent_dir_parent_dir="/";; esac #remove trailing slashes if not root + + shell_histfile_parent_dir_ownership="--reference='${shell_histfile_parent_dir_parent_dir//\'/\'\\\'\'}'" + shell_histfile_ownership="--reference='${shell_histfile_parent_dir//\'/\'\\\'\'}'" + fi + fi + fi + + + + sudo_create_shell_home "$shell_label" \ + "$shell_home" "$create_shell_home_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_shell_home" "$shell_home_ownership" "$shell_home_permission" "$shell_home_file_type_tests" \ + "$shell_rcfile_parent_dir" "$create_shell_rcfile_parent_dir_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_shell_rcfile_parent_dir" "$shell_rcfile_parent_dir_ownership" "$shell_rcfile_parent_dir_permission" "$shell_rcfile_parent_dir_file_type_tests" \ + "$shell_rcfile" "$create_shell_rcfile_if_it_does_not_exist" "$shell_rcfile_value" "$only_set_perms_and_ownership_on_creation_of_shell_rcfile" "$shell_rcfile_ownership" "$shell_rcfile_permission" "$shell_rcfile_file_type_tests" \ + "$shell_histfile_parent_dir" "$create_shell_histfile_parent_dir_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_shell_histfile_parent_dir" "$shell_histfile_parent_dir_ownership" "$shell_histfile_parent_dir_permission" "$shell_histfile_parent_dir_file_type_tests" \ + "$shell_histfile" "$create_shell_histfile_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_shell_histfile" "$shell_histfile_ownership" "$shell_histfile_permission" "$shell_histfile_file_type_tests" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while running \"sudo_create_shell_home\"" + return $return_value + fi + + return 0 + +} + +#sudo_create_shell_home shell_label +#shell_home create_shell_home_if_it_does_not_exist only_set_perms_and_ownership_on_creation_of_shell_home shell_home_ownership shell_home_permission shell_home_file_type_tests +#shell_rcfile_parent_dir create_shell_rcfile_parent_dir_if_it_does_not_exist only_set_perms_and_ownership_on_creation_of_shell_rcfile_parent_dir shell_rcfile_parent_dir_ownership shell_rcfile_parent_dir_permission shell_rcfile_parent_dir_file_type_tests +#shell_rcfile create_shell_rcfile_if_it_does_not_exist shell_rcfile_value only_set_perms_and_ownership_on_creation_of_shell_rcfile shell_rcfile_ownership shell_rcfile_permission shell_rcfile_file_type_tests +#shell_histfile_parent_dir create_shell_histfile_parent_dir_if_it_does_not_exist only_set_perms_and_ownership_on_creation_of_shell_histfile_parent_dir shell_histfile_parent_dir_ownership shell_histfile_parent_dir_permission shell_histfile_parent_dir_file_type_tests +#shell_histfile create_shell_histfile_if_it_does_not_exist only_set_perms_and_ownership_on_creation_of_shell_histfile shell_histfile_ownership shell_histfile_permission shell_histfile_file_type_tests +sudo_create_shell_home() { + + local return_value + + #if parameter count is not 32 + if [ $# -ne 32 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_create_shell_home\"" + return 1 + fi + + local shell_label="$1" + + local shell_home="$2" + local create_shell_home_if_it_does_not_exist="$3" + local only_set_perms_and_ownership_on_creation_of_shell_home="$4" + local shell_home_ownership="$5" + local shell_home_permission="$6" + local shell_home_file_type_tests="$7" + + + local shell_rcfile_parent_dir="$8" + local create_shell_rcfile_parent_dir_if_it_does_not_exist="$9" + local only_set_perms_and_ownership_on_creation_of_shell_rcfile_parent_dir="${10}" + local shell_rcfile_parent_dir_ownership="${11}" + local shell_rcfile_parent_dir_permission="${12}" + local shell_rcfile_parent_dir_file_type_tests="${13}" + + + local shell_rcfile="${14}" + local create_shell_rcfile_if_it_does_not_exist="${15}" + local shell_rcfile_value="${16}" + local only_set_perms_and_ownership_on_creation_of_shell_rcfile="${17}" + local shell_rcfile_ownership="${18}" + local shell_rcfile_permission="${19}" + local shell_rcfile_file_type_tests="${20}" + + + local shell_histfile_parent_dir="${21}" + local create_shell_histfile_parent_dir_if_it_does_not_exist="${22}" + local only_set_perms_and_ownership_on_creation_of_shell_histfile_parent_dir="${23}" + local shell_histfile_parent_dir_ownership="${24}" + local shell_histfile_parent_dir_permission="${25}" + local shell_histfile_parent_dir_file_type_tests="${26}" + + local shell_histfile="${27}" + local create_shell_histfile_if_it_does_not_exist="${28}" + local only_set_perms_and_ownership_on_creation_of_shell_histfile="${29}" + local shell_histfile_ownership="${30}" + local shell_histfile_permission="${31}" + local shell_histfile_file_type_tests="${32}" + + + + #create shell_home if missing and set ownership and permissions + #sudo_create_modify_validate_directory directory_path_label directory_path check_if_absolute_path create_directory_if_it_does_not_exist only_set_perms_and_ownership_on_creation directory_chown_command_options directory_chmod_command_options sub_directories_chown_command_options sub_directories_chmod_command_options sub_files_chown_command_options sub_files_chmod_command_options file_type_tests + sudo_create_modify_validate_directory "${shell_label}_HOME" "$shell_home" 1 "$create_shell_home_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_shell_home" "$shell_home_ownership" "$shell_home_permission" - - - - "$shell_home_file_type_tests" || return $? + + + + #if shell_rcfile is set + if [ ! -z "$shell_rcfile" ]; then + #if shell_home and shell_rcfile_parent_dir are not same + if [[ "$shell_home" != "$shell_rcfile_parent_dir" ]]; then + #create shell_rcfile_parent_dir if missing and set ownership and permissions + #sudo_create_modify_validate_directory directory_path_label directory_path check_if_absolute_path create_directory_if_it_does_not_exist directory_chown_command_options directory_chmod_command_options sub_directories_chown_command_options sub_directories_chmod_command_options sub_files_chown_command_options sub_files_chmod_command_options file_type_tests + sudo_create_modify_validate_directory "${shell_label}_RCFILE_PARENT_DIR" "$shell_rcfile_parent_dir" 1 "$create_shell_rcfile_parent_dir_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_shell_rcfile_parent_dir" "$shell_rcfile_parent_dir_ownership" "$shell_rcfile_parent_dir_permission" - - - - "$shell_rcfile_parent_dir_file_type_tests" || return $? + fi + + #create shell_rcfile if missing and set ownership and permissions + #sudo_create_modify_validate_file file_path_label file_path check_if_absolute_path create_file_if_it_does_not_exist file_creation_command_options file_content only_set_perms_and_ownership_on_creation file_chown_command_options file_chmod_command_options file_type_tests + sudo_create_modify_validate_file "${shell_label}_RCFILE" "$shell_rcfile" 1 "$create_shell_rcfile_if_it_does_not_exist" "%s" "$shell_rcfile_value" "$only_set_perms_and_ownership_on_creation_of_shell_rcfile" "$shell_rcfile_ownership" "$shell_rcfile_permission" "$shell_rcfile_file_type_tests" || return $? + fi + + + + #if shell_histfile is set + if [ ! -z "$shell_histfile" ]; then + #if shell_home and shell_histfile_parent_dir are not same + #and shell_rcfile_parent_dir shell_histfile_parent_dir are not same + if [[ "$shell_home" != "$shell_histfile_parent_dir" ]] && [[ "$shell_rcfile_parent_dir" != "$shell_histfile_parent_dir" ]]; then + #create shell_histfile_parent_dir if missing and set ownership and permissions + #sudo_create_modify_validate_directory directory_path_label directory_path check_if_absolute_path create_directory_if_it_does_not_exist directory_chown_command_options directory_chmod_command_options sub_directories_chown_command_options sub_directories_chmod_command_options sub_files_chown_command_options sub_files_chmod_command_options file_type_tests + sudo_create_modify_validate_directory "${shell_label}_HISTFILE_PARENT_DIR" "$shell_histfile_parent_dir" 1 "$create_shell_histfile_parent_dir_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_shell_histfile_parent_dir" "$shell_histfile_parent_dir_ownership" "$shell_histfile_parent_dir_permission" - - - - "$shell_histfile_parent_dir_file_type_tests" || return $? + fi + + #create shell_histfile if missing and set ownership and permissions + #sudo_create_modify_validate_file file_path_label file_path check_if_absolute_path create_file_if_it_does_not_exist file_creation_command_options file_content only_set_perms_and_ownership_on_creation file_chown_command_options file_chmod_command_options file_type_tests + sudo_create_modify_validate_file "${shell_label}_HISTFILE" "$shell_histfile" 1 "$create_shell_histfile_if_it_does_not_exist" "%s" "" "$only_set_perms_and_ownership_on_creation_of_shell_histfile" "$shell_histfile_ownership" "$shell_histfile_permission" "$shell_histfile_file_type_tests" || return $? + fi + + return 0 + +} + +sudo_setup_termux_tmp_dir() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_setup_termux_tmp_dir" + + local create_termux_tmp_dir_if_it_does_not_exist + local only_set_perms_and_ownership_on_creation_of_termux_tmp_dir + local termux_tmp_dir_permission + local termux_tmp_dir_ownership + local termux_tmp_dir_file_type_tests + + create_termux_tmp_dir_if_it_does_not_exist=1 + only_set_perms_and_ownership_on_creation_of_termux_tmp_dir=1 + termux_tmp_dir_permission="700" + termux_tmp_dir_ownership="--reference='$TERMUX_PREFIX'" + termux_tmp_dir_file_type_tests="drwx" + + + #create TMPDIR if missing + #sudo_create_modify_validate_directory directory_path_label directory_path check_if_absolute_path create_directory_if_it_does_not_exist only_set_perms_and_ownership_on_creation directory_chown_command_options directory_chmod_command_options sub_directories_chown_command_options sub_directories_chmod_command_options sub_files_chown_command_options sub_files_chmod_command_options file_type_tests + sudo_create_modify_validate_directory "TMPDIR" "$TMPDIR" 1 "$create_termux_tmp_dir_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_termux_tmp_dir" "$termux_tmp_dir_ownership" "$termux_tmp_dir_permission" - - - - "$termux_tmp_dir_file_type_tests" || return $? + + return 0 + +} + +sudo_set_sudo_shell_working_dir() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_set_sudo_shell_working_dir" + + #replace "$PREFIX/" or "~/" prefix with termux absolute paths in SUDO_SHELL_WORKING_DIR + sudo_expand_termux_path "SUDO_SHELL_WORKING_DIR" "SUDO_SHELL_WORKING_DIR" "$SUDO_SHELL_WORKING_DIR" "$TERMUX_PREFIX" "$TERMUX_HOME" 1 + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to expand SUDO_SHELL_WORKING_DIR \"$SUDO_SHELL_WORKING_DIR\"" + return $return_value + fi + + #if SUDO_SHELL_WORKING_DIR is not a valid absolute path + if [[ ! "$SUDO_SHELL_WORKING_DIR" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_SHELL_WORKING_DIR \"$SUDO_SHELL_WORKING_DIR\" is not a valid absolute path" + return 1 + fi + + sudo_log 1 "SUDO_SHELL_WORKING_DIR=\"$SUDO_SHELL_WORKING_DIR\"" + + #find the parent directory of the SUDO_SHELL_WORKING_DIR + SUDO_SHELL_WORKING_DIR_BASENAME="${SUDO_SHELL_WORKING_DIR##*/}" #strip longest match of */ from start + SUDO_SHELL_WORKING_DIR_PARENT_DIR="${SUDO_SHELL_WORKING_DIR:0:${#SUDO_SHELL_WORKING_DIR} - ${#SUDO_SHELL_WORKING_DIR_BASENAME}}" #substring from 0 to position of basename + case $SUDO_SHELL_WORKING_DIR_PARENT_DIR in *[!/]*/) SUDO_SHELL_WORKING_DIR_PARENT_DIR=${SUDO_SHELL_WORKING_DIR_PARENT_DIR%"${SUDO_SHELL_WORKING_DIR_PARENT_DIR##*[!/]}"};; *[/]) SUDO_SHELL_WORKING_DIR_PARENT_DIR="/";; esac #remove trailing slashes if not root + + sudo_log 2 "SUDO_SHELL_WORKING_DIR_PARENT_DIR=\"$SUDO_SHELL_WORKING_DIR_PARENT_DIR\"" + + return 0 + +} + +sudo_setup_sudo_shell_working_dir() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_setup_sudo_shell_working_dir" + + local create_shell_working_dir_if_it_does_not_exist + local only_set_perms_and_ownership_on_creation_of_shell_working_dir + local shell_working_dir_permission + local shell_working_dir_ownership + local shell_working_dir_file_type_tests + + create_shell_working_dir_if_it_does_not_exist=1 + only_set_perms_and_ownership_on_creation_of_shell_working_dir=1 + shell_working_dir_permission="700" + + #find the parent directory of the SUDO_SHELL_WORKING_DIR + SUDO_SHELL_WORKING_DIR_BASENAME="${SUDO_SHELL_WORKING_DIR##*/}" #strip longest match of */ from start + SUDO_SHELL_WORKING_DIR_PARENT_DIR="${SUDO_SHELL_WORKING_DIR:0:${#SUDO_SHELL_WORKING_DIR} - ${#SUDO_SHELL_WORKING_DIR_BASENAME}}" #substring from 0 to position of basename + case $SUDO_SHELL_WORKING_DIR_PARENT_DIR in *[!/]*/) SUDO_SHELL_WORKING_DIR_PARENT_DIR=${SUDO_SHELL_WORKING_DIR_PARENT_DIR%"${SUDO_SHELL_WORKING_DIR_PARENT_DIR##*[!/]}"};; *[/]) SUDO_SHELL_WORKING_DIR_PARENT_DIR="/";; esac #remove trailing slashes if not root + + shell_working_dir_ownership="--reference='${SUDO_SHELL_WORKING_DIR_PARENT_DIR//\'/\'\\\'\'}'" + + + #if SUDO_SHELL_WORKING_DIR is the same as TERMUX_FILES or is under it + if [[ "$SUDO_SHELL_WORKING_DIR" == "$TERMUX_FILES" ]] || [[ "$SUDO_SHELL_WORKING_DIR" == "$TERMUX_FILES"/* ]]; then + shell_working_dir_file_type_tests="drwx" + #it might not be possible for the directory to be writable or executable in other paths like in storage + else + shell_working_dir_file_type_tests="dr" + fi + + + #create SUDO_SHELL_WORKING_DIR if missing + #sudo_create_modify_validate_directory directory_path_label directory_path check_if_absolute_path create_directory_if_it_does_not_exist only_set_perms_and_ownership_on_creation directory_chown_command_options directory_chmod_command_options sub_directories_chown_command_options sub_directories_chmod_command_options sub_files_chown_command_options sub_files_chmod_command_options file_type_tests + sudo_create_modify_validate_directory "SUDO_SHELL_WORKING_DIR" "$SUDO_SHELL_WORKING_DIR" 1 "$create_shell_working_dir_if_it_does_not_exist" "$only_set_perms_and_ownership_on_creation_of_shell_working_dir" "$shell_working_dir_ownership" "$shell_working_dir_permission" - - - - "$shell_working_dir_file_type_tests" || return $? + + return 0 + +} + +sudo_setup_sudo_temp_directory() { + + local return_value + + local valid_absolute_path_regex='^(/[^/]+)+$' + + #if remove_previous_sudo_temp_files is enabled + if [[ "$remove_previous_sudo_temp_files" == "1" ]]; then + #if SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY is set to an absolute path and SUDO_TEMP_DIRECTORY_PREFIX is not empty + if [[ "$SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY" =~ $valid_absolute_path_regex ]] && [ ! -z "$SUDO_TEMP_DIRECTORY_PREFIX" ]; then + #remove all directories with the prefix SUDO_TEMP_DIRECTORY_PREFIX + sudo_log_literal 2 "\nRemove all directories with the prefix \"$SUDO_TEMP_DIRECTORY_PREFIX\" in SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY \"$SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY\"" + find "$SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY" -maxdepth 1 -type d -name "$SUDO_TEMP_DIRECTORY_PREFIX*" -exec rm -rf "{}" \; + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while removing all directories with the prefix \"$SUDO_TEMP_DIRECTORY_PREFIX\" in SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY \"$SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY\"" + return $return_value + fi + fi + fi + + #if SETUP_SUDO_TEMP_DIRECTORY is not enabled, then just return + if [[ "$SETUP_SUDO_TEMP_DIRECTORY" != "1" ]]; then + return 0 + fi + + sudo_log_literal 2 "\nRunning sudo_setup_sudo_temp_directory" + + #if SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY is not a valid absolute path + if [[ ! "$SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY \"$SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY\" is not a valid absolute path" + return 1 + fi + + #if SUDO_TEMP_DIRECTORY_FD_PATH is not a valid absolute path + if [[ ! "$SUDO_TEMP_DIRECTORY_FD_PATH" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The SUDO_TEMP_DIRECTORY_FD_PATH \"$SUDO_TEMP_DIRECTORY_FD_PATH\" is not a valid absolute path" + return 1 + fi + + #create a temp directory in SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY to store temp scripts used by the sudo script + #SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY by default would be same as SUDO_SHELL_HOME + #TMPDIR is not used since termux will not be able to remove PREFIX or TMPDIR if the + #directory is not removed automatically at the end of sudo execution and left there + #the parent directory of the scripts must also have root ownership for security reasons + #hence a directory is created to store script files since SUDO_SHELL_HOME itself may not have root ownership + #specially if its the same as TERMUX_HOME + #moreover if '--script-name' is passed, then there may be conflicts between executions since + #temp files will likely share the same parent directory and have the same name + #this will allow each execution of the script to have a separate directory for itself with a random suffix + #scripts will also have an empty directory to use that will be unique for them, which will be removed after execution + SUDO_TEMP_DIRECTORY="$(mktemp -d --tmpdir="$SUDO_TEMP_DIRECTORY_PARENT_DIRECTORY" "$SUDO_TEMP_DIRECTORY_PREFIX.XXXXXX")" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$SUDO_TEMP_DIRECTORY" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "Failure while running mktemp to create SUDO_TEMP_DIRECTORY" + sudo_log_errors "SUDO_TEMP_DIRECTORY=\"$SUDO_TEMP_DIRECTORY\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + #write the value of SUDO_TEMP_DIRECTORY to SUDO_TEMP_DIRECTORY_FD + printf "%s" "$SUDO_TEMP_DIRECTORY" > "$SUDO_TEMP_DIRECTORY_FD_PATH" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failure while writing SUDO_TEMP_DIRECTORY to SUDO_TEMP_DIRECTORY_FD \"$SUDO_TEMP_DIRECTORY_FD\"" + return $return_value + fi + + return 0 + +} + +set_sudo_traps() { + + #set traps for the sudo itself + trap 'sudo_trap' EXIT + trap 'sudo_trap TERM' TERM + trap 'sudo_trap INT' INT + trap 'sudo_trap HUP' HUP + trap 'sudo_trap QUIT' QUIT + + return 0 + +} + +sudo_trap() { + + #the sudo_trap will do the following: + #store the original trap signal in $sudo_exit_code + #remove the EXIT trap so its not called again + #remove temp_directory if set if sudo command failed + #remount rootfs and system partitions back to ro if needed + #if a signal argument was passed, then remove its trap and + #then exit with the original trap signal exit code + #so that parent processes can be notified if necessary + + local sudo_exit_code=$? + trap - EXIT + + [ $sudo_exit_code -ne 0 ] && sudo_remove_sudo_temp_directory + sudo_remount_partitions_for_sudo_shell_homes "ro" + + [ -n "$1" ] && trap - $1; exit $sudo_exit_code + +} + +sudo_remove_sudo_temp_directory() { + + #if SUDO_TEMP_DIRECTORY is a valid absolute path and SU_ENV_COMMAND is set and + #do_not_delete_sudo_temp_directory_on_exit is not enabled and + #sudo command failed + if [[ "$SUDO_TEMP_DIRECTORY" =~ $valid_absolute_path_regex ]] && \ + [ ! -z "$SU_ENV_COMMAND" ] && \ + [[ "$do_not_delete_sudo_temp_directory_on_exit" != "1" ]]; then + #remove SUDO_TEMP_DIRECTORY in case it wasn't already removed + sudo_log_literal 2 "\nRemoving SUDO_TEMP_DIRECTORY in case it wasn't already removed" + sudo_unset_pre_su_variables + $SU_ENV_COMMAND "rm -rf '${SUDO_TEMP_DIRECTORY//\'/\'\\\'\'}'" + sudo_set_post_su_variables + SUDO_TEMP_DIRECTORY="" + fi + +} + +sudo_create_script_command_traps() { + + local return_value + + sudo_log_literal 2 "\nRunning sudo_create_script_command_traps" + + #the sudo_script_trap will do the following: + #store the original trap signal in $sudo_script_exit_code + #call the sudo_script_custom_trap function + #if it exits with 0, then continue normally + #if it exits with 125 'ECANCELED', then do not continue and just return + #if it exits with any other exit code, store it so that we exit + #with that instead of the original trap signal + #remove the EXIT trap so its not called again + #remove temp_directory if set + #if a signal argument was passed, then remove its trap + #send the original trap signal to all the children of the pid + #and exit with the original trap signal exit code or that of the + #sudo_script_custom_trap function so that parent processes can be + #notified if necessary + + SUDO_SCRIPT_COMMAND_TRAPS="" + + #if SUDO_TEMP_DIRECTORY is a valid absolute path and do_not_delete_sudo_temp_directory_on_exit is not enabled + if [[ "$SUDO_TEMP_DIRECTORY" =~ $valid_absolute_path_regex ]] && [[ "$do_not_delete_sudo_temp_directory_on_exit" != "1" ]]; then + #append the function to SUDO_SCRIPT_COMMAND_TRAPS to remove SUDO_TEMP_DIRECTORY + #this may specially be necessary if '-E' is passed + SUDO_SCRIPT_COMMAND_TRAPS_REMOVE_TEMP_DIRECTORY_FUNCTION="sudo_remove_temp_directory" + + SUDO_SCRIPT_COMMAND_TRAPS+=' +export SUDO_SCRIPT_DIR='"'${SUDO_TEMP_DIRECTORY//\'/\'\\\'\'}'"' + +'"$SUDO_SCRIPT_COMMAND_TRAPS_REMOVE_TEMP_DIRECTORY_FUNCTION"'() { + rm -rf '"'${SUDO_TEMP_DIRECTORY//\'/\'\\\'\'}'"' +} +' + else + SUDO_SCRIPT_COMMAND_TRAPS_REMOVE_TEMP_DIRECTORY_FUNCTION="" + fi + + SUDO_SCRIPT_COMMAND_TRAPS+=' +sudo_script_custom_trap() { :; } + +sudo_script_killtree() { + local signal="$1"; local pid="$2"; local cpid + for cpid in $(pgrep -P "$pid"); do sudo_script_killtree "$signal" "$cpid"; done + [[ "$pid" != "$$" ]] && signal="${signal:=15}"; kill "-$signal" "$pid" 2>/dev/null +} + +sudo_script_trap() { + local sudo_script_exit_code=$? + local return_value; sudo_script_custom_trap "$@"; return_value=$? + [ $return_value -eq 125 ] && return 0 + [ $return_value -ne 0 ] && sudo_script_exit_code=$return_value + trap - EXIT '"${SUDO_SCRIPT_COMMAND_TRAPS_REMOVE_TEMP_DIRECTORY_FUNCTION:+$'\n' $SUDO_SCRIPT_COMMAND_TRAPS_REMOVE_TEMP_DIRECTORY_FUNCTION}"' + [ -n "$1" ] && trap - $1 + sudo_script_killtree "$1" $$; exit $sudo_script_exit_code +} +'" +trap 'sudo_script_trap' EXIT +trap 'sudo_script_trap TERM' TERM +trap 'sudo_script_trap INT' INT +trap 'sudo_script_trap HUP' HUP +trap 'sudo_script_trap QUIT' QUIT +" + return 0 + +} + +#sudo_run_file_type_tests_on_path label path log_file_tests_failure_errors show_stat_output_on_file_tests_failure check_if_absolute_path file_type_tests +sudo_run_file_type_tests_on_path() { + + local return_value + + #if parameter count is not 6 + if [ $# -ne 6 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_run_file_type_tests_on_path\"" + return 1 + fi + + local label="$1" + local path="$2" + local log_file_tests_failure_errors="$3" + local show_stat_output_on_file_tests_failure="$4" + local check_if_absolute_path="$5" + local file_type_tests="$6" + + #if file_type_tests is invalid + if [[ ! "$file_type_tests" =~ ^[bcdefgGkhLOprSsuwx]+$ ]]; then + sudo_log_errors "file_type_tests \"$file_type_tests\" passed to \"sudo_run_file_type_tests_on_path\" is not valid" + return 1 + fi + + #if check_if_absolute_path is enabled and path is not a valid absolute path + local valid_absolute_path_regex='^(/[^/]+)+$' + if [[ "$check_if_absolute_path" == "1" ]] && [[ ! "$path" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The \"$label\" file \"$path\" is not an absolute path" + return 2 + fi + + local command="" + + for (( i=0; i<${#file_type_tests}; i++ )); do + file_type_test="${file_type_tests:$i:1}" + + [[ ! -z "$command" ]] && command+=" && " + command+="test -$file_type_test '${path//\'/\'\\\'\'}'" #replace any single quotes `'` with `'\''` to maintain quoting and prevent arbitrary command execution with eval + done + + #if command is empty + if [[ -z "$command" ]]; then + return 0 + fi + + #run all tests + #0 means success, 1 means failure, other exit codes mean some other error + eval "$command" + return_value=$? + if [ $return_value -eq 0 ]; then + return 0 + fi + + if [ $return_value -ne 1 ] || \ + ([ $return_value -eq 1 ] && [[ "$log_file_tests_failure_errors" == "1" ]]); then + local file_type_tests_label="" + + declare -A file_type_tests_labels=( + ["b"]="[block special device]" + ["c"]="[character special device]" + ["d"]="[directory]" + ["e"]="[exists]" + ["f"]="[regular file]" + ["g"]="[set-group-id bit set]" + ["G"]="[owned by the current effective group id]" + ["k"]="[sticky bit set]" + ["h"]="[symbolic link]" + ["L"]="[symbolic link]" + ["O"]="[owned by the current effective user id]" + ["p"]="[pipe]" + ["r"]="[readable]" + ["S"]="[socket]" + ["s"]="[size greater than zero]" + ["u"]="[set-user-id bit set]" + ["w"]="[writable]" + ["x"]="[executable]" + ) + + for (( i=0; i<${#file_type_tests}; i++ )); do + file_type_test="${file_type_tests:$i:1}" + + [[ ! -z "$file_type_tests_label" ]] && file_type_tests_label+=", " + file_type_tests_label+="${file_type_tests_labels[$file_type_test]}" + done + fi + + if [ $return_value -ne 1 ]; then + sudo_log_errors "Failed to run the following test types on the file \"$label\" at path \"$path\": $file_type_tests_label" + else + if [[ "$log_file_tests_failure_errors" == "1" ]]; then + sudo_log_errors "The \"$label\" file at path \"$path\" failed one or more of the following test types: $file_type_tests_label" + if [[ "$show_stat_output_on_file_tests_failure" == "1" ]]; then + current_user_id="$(id -u 2>/dev/null)" + current_user_name="$(id -un 2>/dev/null)" + stat_output="$(stat --format="file type: %F"$'\n'"access: %A (%a)"$'\n'"owner: %U (%u)"$'\n'"group: %G (%g)"$'\n'"selinux context: %C"$'\n'"size: %s" "$path" 2>/dev/null)" + #if stat_output is not empty + if [[ ! -z "$stat_output" ]]; then + sudo_log_errors "current user: $current_user_name ($current_user_id)"$'\n'"$stat_output" + fi + fi + fi + fi + + return $return_value + +} + +#sudo_create_modify_validate_directory directory_path_label directory_path check_if_absolute_path create_directory_if_it_does_not_exist only_set_perms_and_ownership_on_creation directory_chown_command_options directory_chmod_command_options sub_directories_chown_command_options sub_directories_chmod_command_options sub_files_chown_command_options sub_files_chmod_command_options file_type_tests +sudo_create_modify_validate_directory() { + + local return_value + + #if parameter count is not 12 + if [ $# -ne 12 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_create_modify_validate_directory\"" + return 1 + fi + + #sudo_log_literal 2 "\nRunning sudo_create_modify_validate_directory" + + local directory_path_label="$1" + local directory_path="$2" + local check_if_absolute_path="$3" + local create_directory_if_it_does_not_exist="$4" + local only_set_perms_and_ownership_on_creation="$5" + local directory_chown_command_options="$6" + local directory_chmod_command_options="$7" + local sub_directories_chown_command_options="$8" + local sub_directories_chmod_command_options="$9" + local sub_files_chown_command_options="${10}" + local sub_files_chmod_command_options="${11}" + local file_type_tests="${12}" + + local directory_created=0 + + #if create_directory_if_it_does_not_exist is not set to 0 or 1 + if [[ "$create_directory_if_it_does_not_exist" != "0" ]] && [[ "$create_directory_if_it_does_not_exist" != "1" ]]; then + sudo_log_errors "create_directory_if_it_does_not_exist \"$create_directory_if_it_does_not_exist\" passed to \"sudo_create_modify_validate_directory\" is not equal to \"0\" or \"1\"" + return 1 + fi + + #if only_set_perms_and_ownership_on_creation is not set to 0 or 1 + if [[ "$only_set_perms_and_ownership_on_creation" != "0" ]] && [[ "$only_set_perms_and_ownership_on_creation" != "1" ]]; then + sudo_log_errors "only_set_perms_and_ownership_on_creation \"$only_set_perms_and_ownership_on_creation\" passed to \"sudo_create_modify_validate_directory\" is not equal to \"0\" or \"1\"" + return 1 + fi + + #if check_if_absolute_path is enabled and directory_path is not a valid absolute path + local valid_absolute_path_regex='^(/[^/]+)+$' + if [[ "$check_if_absolute_path" == "1" ]] && [[ ! "$directory_path" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The \"$directory_path_label\" directory \"$directory_path\" is not an absolute path" + return 2 + fi + + #check if a non-directory file exists at directory path + if [ -e "$directory_path" ] && [ ! -d "$directory_path" ]; then + sudo_log_errors "A non-directory file exists at $directory_path_label \"$directory_path\"" + return 1 + fi + + #if directory does not exist + if [ ! -d "$directory_path" ]; then + #if create_directory_if_it_does_not_exist is enabled, then create directory + if [[ "$create_directory_if_it_does_not_exist" == "1" ]]; then + sudo_log 2 "Creating $directory_path_label directory at \"$directory_path\"" + mkdir -p "$directory_path" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to create $directory_path_label directory at \"$directory_path\"" + return $return_value + fi + + directory_created=1 + fi + else + sudo_log 2 "The $directory_path_label directory already exists at \"$directory_path\"" + fi + + #check if directory did not already exist or was not created successfully + if [ ! -d "$directory_path" ]; then + sudo_log_errors "Failed to find $directory_path_label directory at \"$directory_path\"" + return 1 + fi + + #if only_set_perms_and_ownership_on_creation is enabled and directory was not created + if [[ "$only_set_perms_and_ownership_on_creation" == "1" ]] && [[ "$directory_created" == "0" ]]; then + sudo_log 2 "Skipping setting ownership and permission for $directory_path_label directory since it was not created" + else + + #set directory ownership + if [[ "$directory_chown_command_options" != "-" ]]; then + IFS=$'\n' + local -a directory_chown_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$directory_chown_command_options") ) + IFS=$' \t\n' + sudo_log 2 "Setting \"${directory_chown_command_options_array[*]}\" ownership to $directory_path_label directory at \"$directory_path\" directory" + chown "${directory_chown_command_options_array[@]}" "$directory_path" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to set ownership of $directory_path_label directory at \"$directory_path\"" + return $return_value + fi + fi + + #set directory permissions + if [[ "$directory_chmod_command_options" != "-" ]]; then + IFS=$'\n' + local -a directory_chmod_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$directory_chmod_command_options") ) + IFS=$' \t\n' + sudo_log 2 "Setting \"${directory_chmod_command_options_array[*]}\" permissions to $directory_path_label directory at \"$directory_path\" directory" + chmod "${directory_chmod_command_options_array[@]}" "$directory_path" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to set permissions of $directory_path_label directory at \"$directory_path\"" + return $return_value + fi + fi + + #set subdirectories ownership + if [[ "$sub_directories_chown_command_options" != "-" ]]; then + IFS=$'\n' + local -a sub_directories_chown_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$sub_directories_chown_command_options") ) + IFS=$' \t\n' + sudo_log 2 "Setting \"${sub_directories_chown_command_options_array[*]}\" ownership to $directory_path_label directory at \"$directory_path\" subdirectories" + find "$directory_path" -type d -print0 | xargs -0 -r -- chown "${sub_directories_chown_command_options_array[@]}" -- + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to set ownership of subdirectories in $directory_path_label directory at \"$directory_path\"" + return $return_value + fi + fi + + #set subdirectories permissions + if [[ "$sub_directories_chmod_command_options" != "-" ]]; then + IFS=$'\n' + local -a sub_directories_chmod_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$sub_directories_chmod_command_options") ) + IFS=$' \t\n' + sudo_log 2 "Setting \"${sub_directories_chmod_command_options_array[*]}\" permissions to $directory_path_label directory at \"$directory_path\" subdirectories" + find "$directory_path" -type d -print0 | xargs -0 -r -- chmod "${sub_directories_chmod_command_options_array[@]}" -- + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to set permissions of subdirectories in $directory_path_label directory at \"$directory_path\"" + return $return_value + fi + fi + + #set subfiles ownership + if [[ "$sub_files_chown_command_options" != "-" ]]; then + IFS=$'\n' + local -a sub_files_chown_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$sub_files_chown_command_options") ) + IFS=$' \t\n' + sudo_log 2 "Setting \"${sub_files_chown_command_options_array[*]}\" ownership to $directory_path_label directory at \"$directory_path\" subfiles" + find "$directory_path" -type f -print0 | xargs -0 -r -- chown "${sub_files_chown_command_options_array[@]}" -- + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to set ownership of subfiles in $directory_path_label directory at \"$directory_path\"" + return $return_value + fi + fi + + #set subfiles permissions + if [[ "$sub_files_chmod_command_options" != "-" ]]; then + IFS=$'\n' + local -a sub_files_chmod_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$sub_files_chmod_command_options") ) + IFS=$' \t\n' + sudo_log 2 "Setting \"${sub_files_chmod_command_options_array[*]}\" permissions to $directory_path_label directory at \"$directory_path\" subfiles" + find "$directory_path" -type f -print0 | xargs -0 -r -- chmod "${sub_files_chmod_command_options_array[@]}" -- + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to set permissions of subfiles in $directory_path_label directory at \"$directory_path\"" + return $return_value + fi + fi + fi + + + #run file type tests with test command + if [[ "$file_type_tests" != "-" ]]; then + #sudo_run_file_type_tests_on_path label path log_file_tests_failure_errors show_stat_output_on_file_tests_failure check_if_absolute_path file_type_tests + sudo_run_file_type_tests_on_path "$directory_path_label" "$directory_path" 1 1 0 "$file_type_tests" || return $? + fi + + + return 0 + +} + +#sudo_create_modify_validate_file file_path_label file_path check_if_absolute_path create_file_if_it_does_not_exist file_creation_command_options file_content only_set_perms_and_ownership_on_creation file_chown_command_options file_chmod_command_options file_type_tests +sudo_create_modify_validate_file() { + + local return_value + + #if parameter count is not 10 + if [ $# -ne 10 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_create_modify_validate_file\"" + return 1 + fi + + #sudo_log_literal 2 "\nRunning sudo_create_modify_validate_file" + + local file_path_label="$1" + local file_path="$2" + local check_if_absolute_path="$3" + local create_file_if_it_does_not_exist="$4" + local file_creation_command_options="$5" + local file_content="$6" + local only_set_perms_and_ownership_on_creation="$7" + local file_chown_command_options="$8" + local file_chmod_command_options="$9" + local file_type_tests="${10}" + + local file_created=0 + + #if create_file_if_it_does_not_exist is not set to 0 or 1 + if [[ "$create_file_if_it_does_not_exist" != "0" ]] && [[ "$create_file_if_it_does_not_exist" != "1" ]]; then + sudo_log_errors "create_file_if_it_does_not_exist \"$create_file_if_it_does_not_exist\" passed to \"sudo_create_modify_validate_file\" is not equal to \"0\" or \"1\"" + return 1 + fi + + #if only_set_perms_and_ownership_on_creation is not set to 0 or 1 + if [[ "$only_set_perms_and_ownership_on_creation" != "0" ]] && [[ "$only_set_perms_and_ownership_on_creation" != "1" ]]; then + sudo_log_errors "only_set_perms_and_ownership_on_creation \"$only_set_perms_and_ownership_on_creation\" passed to \"sudo_create_modify_validate_file\" is not equal to \"0\" or \"1\"" + return 1 + fi + + #if check_if_absolute_path is enabled and file_path is not a valid absolute path + local valid_absolute_path_regex='^(/[^/]+)+$' + if [[ "$check_if_absolute_path" == "1" ]] && [[ ! "$file_path" =~ $valid_absolute_path_regex ]]; then + sudo_log_errors "The \"$file_path_label\" file \"$file_path\" is not an absolute path" + return 2 + fi + + #check if a non-file file exists at file path + if [ -e "$file_path" ] && [ ! -f "$file_path" ]; then + sudo_log_errors "A non-regular file exists at $file_path_label \"$file_path\"" + return 1 + fi + + #if it does not exist + if [ ! -f "$file_path" ]; then + #if create_file_if_it_does_not_exist is enabled, then create file + if [[ "$create_file_if_it_does_not_exist" == "1" ]]; then + + sudo_log 2 "Creating $file_path_label \"$file_path\" file" + local -a file_creation_command_options_array + if [[ "$file_creation_command_options" != "-" ]]; then + IFS=$'\n' + file_creation_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$file_creation_command_options") ) + IFS=$' \t\n' + fi + + #if file_creation_options is set, then pass it to printf + if [ ${#file_creation_command_options_array[@]} -ne 0 ]; then + printf "${file_creation_command_options_array[@]}" "$file_content" > "$file_path" + else + printf "$file_content" > "$file_path" + fi + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to create $file_path_label file at \"$file_path\"" + return $return_value + fi + + file_created=1 + fi + else + sudo_log 2 "The $file_path_label file already exists at \"$file_path\"" + fi + + #check if file did not already exist or was not created successfully + if [ ! -f "$file_path" ]; then + sudo_log_errors "Failed to find $file_path_label file at \"$file_path\"" + return 1 + fi + + #if only_set_perms_and_ownership_on_creation is enabled and file was not created + if [[ "$only_set_perms_and_ownership_on_creation" == "1" ]] && [[ "$file_created" == "0" ]]; then + sudo_log 2 "Skipping setting ownership and permission for $file_path_label file since it was not created" + else + #set file ownership + if [[ "$file_chown_command_options" != "-" ]]; then + IFS=$'\n' + local -a file_chown_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$file_chown_command_options") ) + IFS=$' \t\n' + sudo_log 2 "Setting \"${file_chown_command_options_array[*]}\" ownership to $file_path_label \"$file_path\" file" + chown "${file_chown_command_options_array[@]}" "$file_path" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to set ownership of $file_path_label file at \"$file_path\"" + return $return_value + fi + fi + + #set file permissions + if [[ "$file_chmod_command_options" != "-" ]]; then + IFS=$'\n' + local -a file_chmod_command_options_array=( $(xargs -r -n1 -- printf -- "%s\n" <<< "$file_chmod_command_options") ) + IFS=$' \t\n' + sudo_log 2 "Setting \"${file_chmod_command_options_array[*]}\" permissions to $file_path_label \"$file_path\" file" + chmod "${file_chmod_command_options_array[@]}" "$file_path" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to set permissions of $file_path_label file at \"$file_path\"" + return $return_value + fi + fi + fi + + + #run file type tests with test command + if [[ "$file_type_tests" != "-" ]]; then + #sudo_run_file_type_tests_on_path label path log_file_tests_failure_errors show_stat_output_on_file_tests_failure check_if_absolute_path file_type_tests + sudo_run_file_type_tests_on_path "$file_path_label" "$file_path" 1 1 0 "$file_type_tests" || return $? + fi + + + return 0 + +} + +#sudo_expand_termux_path variable_name label path termux_prefix termux_home canonicalize_path +sudo_expand_termux_path() { + + local return_value + + #if parameter count is not 6 + if [ $# -ne 6 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_expand_termux_path\"" + return 1 + fi + + local variable_name="$1" + local label="$2" + local path_to_expand="$3" + local termux_prefix="$4" + local termux_home="$5" + local canonicalize_path="$6" + + [[ "$path_to_expand" == "\$PREFIX/" ]] && path_to_expand="$termux_prefix" #replace '$PREFIX/' with "$termux_prefix" + [[ "$path_to_expand" == "~/" ]] && path_to_expand="$termux_home" #replace '~/' with "$termux_home" + + path_to_expand=${path_to_expand/#\$PREFIX\//$termux_prefix\/} #replace '$PREFIX/*' with "$termux_prefix/*" + path_to_expand=${path_to_expand/#~\//$termux_home\/} #replace '~/*' with "$termux_home/*" + + #if canonicalize_path equals "1" or (canonicalize_path equals "2" and path_to_expand is not a symlink + if [[ "$canonicalize_path" == "1" ]] || ([[ "$canonicalize_path" == "2" ]] && [ ! -L "$path_to_expand" ]); then + path_to_expand="$(readlink -m -- "$path_to_expand")" + fi + + local valid_bash_variable_name_regex='^[a-zA-Z][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + #if variable_name does not equal "path_to_expand" and is valid bash variable_name + if [[ "$variable_name" != "path_to_expand" ]] && [[ "$variable_name" =~ $valid_bash_variable_name_regex ]]; then + #set variable_name to path_to_expand + printf -v "$variable_name" "%s" "$path_to_expand" + else + sudo_log_errors "variable_name \"$1\" passed to \"sudo_expand_termux_path\" equals \"path_to_expand\" or is not a valid bash variable name" + return 1 + fi + +} + +#the path will be first expanded +#if the path is an absolute path and an executable then that is returned, otherwise function fails with 112 +#if the path is not an absolute path, then it is searched in current working directory by "readlink -m" and also in all the paths in paths_to_check_for_executable list (ideally $PATH variable) +#if an executable is found, then that is returned, otherwise function fails with 112 +#sudo_find_absolute_path_for_executable_and_validate variable_name label path paths_to_check_for_executable termux_prefix termux_home output_path_to_stdout +sudo_find_absolute_path_for_executable_and_validate() { + + local return_value + + #if parameter count is not 7 + if [ $# -ne 7 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_find_absolute_path_for_executable_and_validate\"" + return 1 + fi + + local variable_name="$1" + local label="$2" + local path="$3" + local paths_to_check_for_executable="$4" + local termux_prefix="$5" + local termux_home="$6" + local output_path_to_stdout="$7" + + local executable_found=0 + local absolute_path + + #replace "$PREFIX/" or "~/" prefix with termux absolute paths in path + sudo_expand_termux_path "absolute_path" "${label}_PATH" "$path" "$termux_prefix" "$termux_home" 1 + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to expand ${label}_PATH \"$path\"" + return $return_value + fi + + [[ "$output_path_to_stdout" == "1" ]] || sudo_log 2 "${label}_ABSOLUTE_PATH=\"$absolute_path\"" + + test -f "$absolute_path" && test -r "$absolute_path" && test -x "$absolute_path" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 1 ]; then + sudo_log_errors "Failed to run the following test types on the file \"${label}_PATH\" at path \"$absolute_path\": [regular file], [readable], [executable]" + return $return_value + elif [ $return_value -eq 0 ]; then + [[ "$output_path_to_stdout" == "1" ]] || sudo_log 2 "Executable found at \"$absolute_path\"" + executable_found=1 + fi + + #if executable still not found and original path was not an absolute path, then check if path exists in PATH_TO_EXPORT + local valid_absolute_path_regex='^(/[^/]+)+$' + if [[ "$executable_found" == "0" ]] && [[ ! "$path" =~ $valid_absolute_path_regex ]]; then + #check if paths_to_check_for_executable is valid + local invalid_value_for_path_variable_regex='^:|:$|.*::.*|.*'$'\n''.*' + if [[ "$paths_to_check_for_executable" =~ $invalid_value_for_path_variable_regex ]]; then + sudo_log_errors "PATHS_TO_CHECK_FOR_EXECUTABLE \"$paths_to_check_for_executable\" is invalid, it cannot start or end with a colon, contain two consecutive colons \":\" or contain newline characters" + return 1 + fi + + [[ "$output_path_to_stdout" == "1" ]] || sudo_log 2 "Checking for \"$path\" executable at following paths: \"$paths_to_check_for_executable\"" + + #check if path is a binary or any executable in paths_to_check_for_executable + for path_to_check_for_executable in ${paths_to_check_for_executable//:/ }; do + absolute_path="$path_to_check_for_executable/$path" + [[ "$output_path_to_stdout" == "1" ]] || sudo_log 2 "\"$absolute_path\"" + + test -f "$absolute_path" && test -r "$absolute_path" && test -x "$absolute_path" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 1 ]; then + sudo_log_errors "Failed to run the following test types on the file \"${label}_PATH\" at path \"$absolute_path\": [regular file], [readable], [executable]" + return $return_value + elif [ $return_value -eq 0 ]; then + [[ "$output_path_to_stdout" == "1" ]] || sudo_log 2 "Executable found at \"$absolute_path\"" + executable_found=1 + break + fi + done + fi + + #if executable is not found, then exit with error + #since this is the final path + if [[ "$executable_found" != "1" ]]; then + return 112 + fi + + #if output_path_to_stdout is enabled, then create file + if [[ "$output_path_to_stdout" == "1" ]]; then + #echo absolute_path to stdout + echo "$absolute_path" + else + local valid_bash_variable_name_regex='^[a-zA-Z][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + #if variable_name does not equal "absolute_path" and is valid bash variable_name + if [[ "$variable_name" != "absolute_path" ]] && [[ "$variable_name" =~ $valid_bash_variable_name_regex ]]; then + #set variable_name to absolute_path + printf -v "$variable_name" "%s" "$absolute_path" + else + sudo_log_errors "variable_name \"$1\" passed to \"sudo_find_absolute_path_for_executable_and_validate\" equals \"absolute_path\" or is not a valid bash variable name" + return 1 + fi + fi + +} + +#validate if path is under TERMUX_FILES and is not backlisted +#sudo_validate_path_is_a_whitelisted_path_under_termux_files label path +sudo_validate_path_is_a_whitelisted_path_under_termux_files() { + + local return_value + + #if parameter count is not 2 + if [ $# -ne 2 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_validate_path_is_a_whitelisted_path_under_termux_files\"" + return 1 + fi + + local label="$1" + local path="$2" + + local valid_path_under_termux_files_regex='^'"$TERMUX_FILES"'/.+' + local blacklisted_path_in_termux_files_regex='^'"$TERMUX_FILES"'[\/]+(('"$TERMUX_HOME_BASENAME"'[\/]+\.(cache|config|local|termux))|('"$TERMUX_PREFIX_BASENAME"')|('"$TERMUX_PREFIX_BASENAME"'[\/]+.*))([\/]+)?$' + + #if path same as TERMUX_FILES + if [[ "$path" == "$TERMUX_FILES" ]]; then + sudo_log 2 "$label \"$path\" is the same as TERMUX_FILES" + return 2 + #if path is set to an absolute path under TERMUX_FILES + elif [[ "$path" =~ $valid_path_under_termux_files_regex ]]; then + #if path is set to a blacklist path under TERMUX_FILES + if [[ "$path" =~ $blacklisted_path_in_termux_files_regex ]]; then + sudo_log 2 "$label \"$path\" matches one of following blacklisted paths:" + sudo_log 2 "\"$TERMUX_HOME/.{cache,config,local,termux}\", \"$TERMUX_PREFIX/*\"" + return 2 + else + return 0 + fi + else + return 3 + fi + +} + +#sudo_parse_and_validate_path_variable variable_name label path remove_duplicates +sudo_parse_and_validate_path_variable() { + + local return_value + + #if parameter count is not 4 + if [ $# -ne 4 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_parse_and_validate_path_variable\"" + return 1 + fi + + local variable_name="$1" + local label="$2" + local path_to_parse="$3" + local remove_duplicates="$4" + + #if remove_duplicates is enabled + if [[ "$remove_duplicates" == "1" ]]; then + #remove duplicates from path_to_parse + path_to_parse="$(printf "%s" "$path_to_parse" | awk -v RS=: -v ORS= '!a[$0]++ {if (NR>1) printf(":"); printf("%s", $0) }' )" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to remove duplicates from $label \"$path_to_parse\"" + return $return_value + fi + fi + + #check if path_to_parse is valid + local invalid_value_for_path_variable_regex='^:|:$|.*::.*|.*'$'\n''.*' + if [[ "$path_to_parse" =~ $invalid_value_for_path_variable_regex ]]; then + sudo_log_errors "$label \"$path_to_parse\" is invalid, it cannot start or end with a colon, contain two consecutive colons \":\" or contain newline characters" + return 1 + fi + + local valid_bash_variable_name_regex='^[a-zA-Z][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + #if variable_name does not equal "path_to_parse" and is valid bash variable_name + if [[ "$variable_name" != "path_to_parse" ]] && [[ "$variable_name" =~ $valid_bash_variable_name_regex ]]; then + #set variable_name to path + printf -v "$variable_name" "%s" "$path_to_parse" + else + sudo_log_errors "variable_name \"$1\" passed to \"sudo_parse_and_validate_path_variable\" equals \"path_to_parse\" or is not a valid bash variable name" + return 1 + fi + +} + +#sudo_remount_partitions_for_sudo_shell_homes remount_mode +#remount_mode must be "ro" or "rw" +sudo_remount_partitions_for_sudo_shell_homes() { + + local return_value + + #if dry_run_sudo is enabled, then just return + if [[ "$dry_run_sudo" == "1" ]]; then + return 0 + fi + + #if remount_mode equals "ro" and do_not_remount_partitions_back_to_ro_after_sudo is enabled, then just return + if [[ "$remount_mode" == "ro" ]] && [[ "$do_not_remount_partitions_back_to_ro_after_sudo" == "1" ]]; then + return 0 + fi + + sudo_log_literal 2 "\nRunning sudo_remount_partitions_for_sudo_shell_homes" + + #if parameter count is not 1 + if [ $# -ne 1 ]; then + sudo_log_errors "Invalid parameter count to \"sudo_remount_partitions_for_sudo_shell_homes\"" + return 1 + fi + + local remount_mode="$1" + if [[ "$remount_mode" != "ro" ]] && [[ "$remount_mode" != "rw" ]]; then + sudo_log_errors "remount_mode \"$remount_mode\" passed to \"sudo_remount_partitions_for_sudo_shell_homes\" does not equal \"ro\" or \"rw\"" + return 1 + fi + + #if rootfs_partition_dependent_paths is set + if [ ! -z "$rootfs_partition_dependent_paths" ]; then + #if remount_mode equals "rw" + if [[ "$remount_mode" == "rw" ]]; then + #remount android rootfs partition as rw + sudo_log 2 "Remounting rootfs \"/\" partition as rw to be used for$rootfs_partition_dependent_paths" + sudo_remount_partition "rootfs" "rw" 0 + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 112 ]; then + sudo_log_errors "Failed to mount rootfs \"/\" partition as rw to be used for$rootfs_partition_dependent_paths" + return $return_value + fi + + rootfs_partition_remounted=1 + #if remount_mode equals "ro" + elif [[ "$remount_mode" == "ro" ]]; then + #if rootfs_partition_remounted is enabled + if [[ "$rootfs_partition_remounted" == "1" ]]; then + #mount rootfs partition back as as ro + #if rootfs partition was not mounted as ro before running sudo command it will not be remounted to ro + #unless force_remount_partitions_back_to_ro_after_sudo is enabled + sudo_remount_partition "rootfs" "ro" "$force_remount_partitions_back_to_ro_after_sudo" + rootfs_partition_remounted=0 + fi + fi + fi + + + #if system_partition_dependent_paths is set + if [ ! -z "$system_partition_dependent_paths" ]; then + #if remount_mode equals "rw" + if [[ "$remount_mode" == "rw" ]]; then + #remount android system partition as rw + sudo_log 2 "Remounting system \"/system\" partition as rw to be used for$system_partition_dependent_paths" + sudo_remount_partition "system" "rw" 0 + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 112 ]; then + sudo_log_errors "Failed to mount system \"/system\" partition as rw to be used for$system_partition_dependent_paths" + return $return_value + fi + + system_partition_remounted=1 + #if remount_mode equals "ro" + elif [[ "$remount_mode" == "ro" ]]; then + #if system_partition_remounted is enabled + if [[ "$system_partition_remounted" == "1" ]]; then + #mount system partition back as as ro + #if system partition was not mounted as ro before running sudo command it will not be remounted to ro + #unless force_remount_partitions_back_to_ro_after_sudo is enabled + sudo_remount_partition "system" "ro" "$force_remount_partitions_back_to_ro_after_sudo" + system_partition_remounted=0 + fi + fi + fi + + return 0 + +} + +#sudo_get_unsed_file_descriptor variable_name +sudo_get_unsed_file_descriptor() { + + local return_value + + local variable_name="$1" + + local fd=2 + + #ulimit -n returns per-process max file descriptors that can be opened + #but not using it to reduce external call + #instead using the default 256 set by most systems for compatibility + #real limit is likely to be higher like 1024, depending on android kernel version + #local max=$(ulimit -n || echo 256) + local max=256 + + #iterate over all fds and find one for which dupe fails and use that + while ((++fd < max)); do + ! true <&$fd && break + done 2>/dev/null + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_log_errors "Failed to find an unused descriptor between 2-$max. last fd: '$fd'" + return $return_value + fi + + local valid_bash_variable_name_regex='^[a-zA-Z][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + #if variable_name does not equal "fd" and is valid bash variable_name + if [[ "$variable_name" != "fd" ]] && [[ "$variable_name" =~ $valid_bash_variable_name_regex ]]; then + #set variable_name to fd + printf -v "$variable_name" "%s" "$fd" + else + sudo_log_errors "variable_name \"$1\" passed to \"sudo_get_unsed_file_descriptor\" equals \"fd\" or is not a valid bash variable name" + return 1 + fi + +} + +#remount_partition must be "rootfs" or "system" +#remount_mode must be "ro" or "rw" +#force_remount must be "0" or "1" +#sudo_remount_partition remount_partition remount_mode force_r`emount +sudo_remount_partition() { + + local return_value + + #if parameter count is not 3 + if [ $# -ne 3 ]; then + sudo_log_errors "Invalid parameter count to \"remount_android_rootfs_for_cldia\"" + return 1 + fi + + local remount_partition="$1" + if [[ "$remount_partition" != "rootfs" ]] && [[ "$remount_partition" != "system" ]]; then + sudo_log_errors "remount_partition \"$remount_partition\" passed to \"sudo_remount_partition\" does not equal \"rootfs\" or \"system\"" + return 1 + fi + + local remount_mode="$2" + if [[ "$remount_mode" != "ro" ]] && [[ "$remount_mode" != "rw" ]]; then + sudo_log_errors "remount_mode \"$remount_mode\" passed to \"sudo_remount_partition\" does not equal \"ro\" or \"rw\"" + return 1 + fi + + local force_remount="$3" + if [[ "$force_remount" != "0" ]] && [[ "$force_remount" != "1" ]]; then + sudo_log_errors "remount_mode \"$remount_mode\" passed to \"sudo_remount_partition\" does not equal \"0\" or \"1\"" + return 1 + fi + + local remount_partition_mount_point + local current_mount_mode_command_output + local current_mount_mode + local remount_partition_previous_state_ro + local remount_partition_previous_state_ro_variable_name + local mountBinaryPath + local remount_command_output + + #search for mount executable + for f in "$SYS_XBIN" "$SYS_BIN"; do + test -x "$f/mount" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 1 ]; then + sudo_log_errors "test command for \"$f/mount\" failed" + return $return_value + fi + + #if mount executable exists at "$f/mount" + if [ $return_value -eq 0 ]; then + mountBinaryPath="$f/mount" + break + fi + done + + #if mount executable not found + if [ -z "$mountBinaryPath" ]; then + sudo_log_errors "\nCannot find mount executable" + return 1 + fi + + #if remount_partition equals "rootfs" + if [[ "$remount_partition" == "rootfs" ]]; then + remount_partition_mount_point="/" + #if remount_partition equals "system" + elif [[ "$remount_partition" == "system" ]]; then + remount_partition_mount_point="/system" + else + sudo_log_errors "remount_partition \"$remount_partition\" not handled" + return 1 + fi + + #find entry of the mount point in /proc/mounts + #there is an additional "^[^ ]+ " before the path so that only mount points are matched and not the mount sources + #there is an additional " " after the path so that grep does not match mounts on subdirectories of the mount point + #or any enteries in the same directory as the mount point with the same basename but with one or more characters at the end + remount_partition_mount_entry="$(grep -m 1 -E "^[^ ]+ $remount_partition_mount_point " /proc/mounts)" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 1 ]; then + sudo_log_errors "$remount_partition_mount_entry" + sudo_log_errors "Failed to run grep to get remount_partition_mount_entry from /proc/mounts" + return $return_value + fi + sudo_log 2 "remount_partition_mount_entry = \"$remount_partition_mount_entry\"" + + #if remount_partition_mount_entry is empty, then mount_point is not mounted + if [[ -z "$remount_partition_mount_entry" ]]; then + sudo_log 2 "No mount entries found for \"$remount_partition_mount_point\" in /proc/mounts" + sudo_log 2 "\"$remount_partition_mount_point\" is not mounted" + return 112 + fi + + #check if remount_partition_mount_point is currently mounted as the same mount_mode as remount_mode or not + current_mount_mode_command_output="$(echo "$remount_partition_mount_entry" | grep -E "^[^ ]+ $remount_partition_mount_point [^ ]+ ro,")" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 1 ]; then + sudo_log_errors "$current_mount_mode_command_output" + sudo_log_errors "Failed to find if $remount_partition_mount_point is mounted as $remount_mode" + return $return_value + #if current_mount_mode equals "ro" + elif [ $return_value -eq 0 ]; then + current_mount_mode="ro" + #if current_mount_mode equals "rw" + else + current_mount_mode="rw" + fi + + remount_partition_previous_state_ro_variable_name="${remount_partition}_previous_state_ro" + + #if remount_mode equals "rw" + if [[ "$remount_mode" == "rw" ]]; then + #set previous remount state of remount_partition to be used later + #note that $remount_partition will be evaluated to the value in the variable like "rootfs" or "system" + sudo_log 2 "Setting $remount_partition_previous_state_ro_variable_name to $return_value" + eval $remount_partition_previous_state_ro_variable_name=$return_value + fi + + #if remount_mode is equal to current_mount_mode + if [[ "$remount_mode" == "$current_mount_mode" ]]; then + sudo_log 2 "\"$remount_partition_mount_point\" is already mounted as $remount_mode" + + #if force_remount is not enabled, then exit + if [[ $force_remount != "1" ]]; then + return 0 + else + sudo_log 2 "Force remounting \"$remount_partition_mount_point\" as $remount_mode" + fi + else + sudo_log 2 "\"$remount_partition_mount_point\" is mounted as $current_mount_mode" + fi + + #if remount_mode equals "ro" + if [[ "$remount_mode" == "ro" ]]; then + #find previous remount state of remount_partition + #note that $remount_partition will be evaluated to the value in the variable like "rootfs" or "system" + eval remount_partition_previous_state_ro="\$$remount_partition_previous_state_ro_variable_name" + + #if rootfs was previously mounted as rw and force_remount is not set to 1 + #remount_partition_previous_state_ro will be 0 if it was ro, 1 if it was rw + if [[ "$remount_partition_previous_state_ro" == "1" ]] && [[ $force_remount -ne 1 ]]; then + sudo_log 2 "Skipping remount \"$remount_partition_mount_point\" as ro since it was previously mounted as rw" + return 0 + fi + fi + + #if SU or SU_ENV_COMMAND is not set + #this function is sourced and called by the sudo_tests script in which case sudo_main is not called to set SU and SU_ENV_COMMAND + if [ -z "$SU" ] || [ -z "$SU_ENV_COMMAND" ]; then + #set SU and SU_BIN_PATH + sudo_set_su_variables || return $? + + #set SU_ENV_COMMAND + sudo_set_su_env_command || return $? + fi + + #remount remount_partition_mount_point as remount_mode in global namespace + sudo_log 2 $'\n'"Remounting $remount_partition_mount_point as $remount_mode" + + sudo_unset_pre_su_variables + remount_command_output="$($SU_ENV_COMMAND "\"$mountBinaryPath\" -o \"$remount_mode,remount\" \"$remount_partition_mount_point\"" 2>&1 < /dev/null)" + return_value=$? + sudo_set_post_su_variables + #if mount_point is busy error received, then just ignore + if [ $return_value -eq 32 ] && [[ "$remount_mode" == "ro" ]] && [[ "$remount_command_output" == *"$remount_partition_mount_point is busy"* ]]; then + sudo_log 2 "Failed to remount $remount_partition_mount_point as $remount_mode, since its busy" + sudo_log 2 "Ignoring error and force setting result_code to \"0\"" + return 0 + #else if non zero exit code received or output is set, then exit with error + #in some cases exit code is 0, even though mount actually fails like if partition is read only + elif [ $return_value -ne 0 ] || [ ! -z "$remount_command_output" ]; then + sudo_log_errors "$remount_command_output" + sudo_log_errors "Failed to remount $remount_partition_mount_point as $remount_mode" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + else + sudo_log 2 "Remount $remount_partition_mount_point as $remount_mode successful" + fi + + return $return_value + +} + +#sudo_replace_comma_alternative_chars_with_commas_in_string string +sudo_replace_comma_alternative_chars_with_commas_in_string() { + + local valid_bash_variable_name_regex='^[a-zA-Z][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + #if $1 is valid bash variable_name + if [[ "$1" =~ $valid_bash_variable_name_regex ]]; then + #set variable_name stored in $1 to variable_value in $2 after replacing all COMMA_ALTERNATIVE characters with a comma ',' in $2 + printf -v "$1" "%s" "${2//$COMMA_ALTERNATIVE/,}" + else + sudo_log_errors "variable_name \"$1\" passed to \"sudo_replace_comma_alternative_chars_with_commas_in_string\" is not a valid bash variable name" + fi + +} + +#sudo_trim_trailing_newlines variable_name variable_value +sudo_trim_trailing_newlines() { + + local extglob_was_unset=1 + shopt extglob >/dev/null && extglob_was_unset=0 #check if 'extglob' is currently set + (( extglob_was_unset )) && shopt -s extglob #set 'extglob', if currently unset + + local valid_bash_variable_name_regex='^[a-zA-Z][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + #if $1 is valid bash variable_name + if [[ "$1" =~ $valid_bash_variable_name_regex ]]; then + #set variable_name stored in $1 to variable_value in $2 without trailing newline and carriage return characters + printf -v "$1" "%s" "${2%%*([$'\r\n'])}" + else + sudo_log_errors "variable_name \"$1\" passed to \"sudo_trim_trailing_newlines\" is not a valid bash variable name" + fi + + (( extglob_was_unset )) && shopt -u extglob #unset 'extglob', if previously unset + +} + +process_sudo_parameters() { + + #parse options to sudo command + while getopts ":hvabBcdDeEfFHilLnNoOpPrRsS-:" opt; do + case ${opt} in + -) + long_optargs="${OPTARG#*=}" + case "${OPTARG}" in + help-extra) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + show_sudo_help_extra + $sudo_exit_command 0 + ;; + help-extra*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + help) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + show_sudo_help + $sudo_exit_command 0 + ;; + help*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + version) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + echo "$version" + $sudo_exit_command 0 + ;; + version*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + comma-alternative=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + COMMA_ALTERNATIVE="$val" + ;; + comma-alternative | comma-alternative=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + dry-run) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + dry_run_sudo=1 + ;; + dry-run*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + export-paths=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + sudo_trim_trailing_newlines "ADDITIONAL_PATHS_TO_EXPORT" "$val" + ;; + export-paths | export-paths=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + export-ld-lib-paths=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + sudo_trim_trailing_newlines "ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT" "$val" + ;; + export-ld-lib-paths | export-ld-lib-paths=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + force-remount-ro) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + force_remount_partitions_back_to_ro_after_sudo=1 + ;; + force-remount-ro*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + hold) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + HOLD_STRING_AFTER_SUDO="" + hold_after_sudo=1 + ;; + hold=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + sudo_trim_trailing_newlines "HOLD_STRING_AFTER_SUDO" "$val" + hold_after_sudo=1 + ;; + hold=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + hold-if-fail) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + hold_only_on_failure=1 + ;; + hold-if-fail*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + list-interactive-shells) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + echo "$SUDO_SUPPORTED_INTERACTIVE_SHELLS" + $sudo_exit_command 0 + ;; + list-interactive-shells*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + list-script-shells) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + echo "$SUDO_SUPPORTED_SCRIPT_SHELLS" + $sudo_exit_command 0 + ;; + list-script-shells*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + no-create-hist) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + sudo_shells_automatically_create_history_files=0 + ;; + no-create-hist*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + no-create-rc) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + sudo_shells_automatically_create_rc_files=0 + ;; + no-create-rc*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + no-hist) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + sudo_shells_history_enabled=0 + ;; + no-hist*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + no-log-args) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + disable_arguments_logging=1 + ;; + no-log-args*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + no-remount-ro) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + do_not_remount_partitions_back_to_ro_after_sudo=1 + ;; + no-remount-ro*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + keep-temp) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + do_not_delete_sudo_temp_directory_on_exit=1 + ;; + keep-temp*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + post-shell=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + sudo_trim_trailing_newlines "SUDO_POST_SHELL" "$val" + ;; + post-shell | post-shell=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + post-shell-home=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + sudo_trim_trailing_newlines "SUDO_POST_SHELL_HOME" "$val" + custom_sudo_post_shell_home_set=1 + ;; + post-shell-home | post-shell-home=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + post-shell-options=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + IFS=$'\n' val_arr=( $(xargs -r -n1 -- printf -- "%s\n" <<<"$val") ) + IFS=$' \t\n' + SUDO_POST_SHELL_INTERACTIVE_ADDITIONAL_COMMAND_OPTIONS=( "${val_arr[@]}" ) + ;; + post-shell-options | post-shell-options=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + post-shell-post-commands=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + ADDITIONAL_SUDO_POST_SHELL_POST_COMMANDS_TO_RUN="$val" + ;; + post-shell-post-commands | post-shell-post-commands=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + post-shell-pre-commands=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + ADDITIONAL_SUDO_POST_SHELL_PRE_COMMANDS_TO_RUN="$val" + ;; + post-shell-pre-commands | post-shell-pre-commands=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + post-shell-stdin-string=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + SUDO_POST_SHELL_STDIN_STRING="$val" + ;; + post-shell-stdin-string | post-shell-stdin-string=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + remove-prev-temp) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + remove_previous_sudo_temp_files=1 + ;; + remove-prev-temp*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + script-decode) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + force_use_temp_script_file_for_core_script=1 + decode_core_script_content=1 + ;; + script-decode*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + script-name=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + sudo_trim_trailing_newlines "SUDO_CORE_SCRIPT_TEMP_FILENAME" "$val" + ;; + script-name | script-name=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + script-redirect=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + sudo_trim_trailing_newlines "SUDO_CORE_SCRIPT_REDIRECT_MODE" "$val" + ;; + script-redirect | script-redirect=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + shell=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + sudo_trim_trailing_newlines "SUDO_SHELL" "$val" + ;; + shell | shell=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + shell-home=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + sudo_trim_trailing_newlines "SUDO_SHELL_HOME" "$val" + custom_sudo_shell_home_set=1 + ;; + shell-home | shell-home=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + shell-options=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + IFS=$'\n' val_arr=( $(xargs -r -n1 -- printf -- "%s\n" <<<"$val") ) + IFS=$' \t\n' + SUDO_SHELL_ADDITIONAL_COMMAND_OPTIONS=( "${val_arr[@]}" ) + ;; + shell-options | shell-options=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + shell-post-commands=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + ADDITIONAL_SUDO_SHELL_POST_COMMANDS_TO_RUN="$val" + ;; + shell-post-commands | shell-post-commands=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + shell-pre-commands=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + ADDITIONAL_SUDO_SHELL_PRE_COMMANDS_TO_RUN="$val" + ;; + shell-pre-commands | shell-pre-commands=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + shell-stdin-string=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + SUDO_SHELL_STDIN_STRING="$val" + ;; + shell-stdin-string | shell-stdin-string=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + sleep=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + sudo_trim_trailing_newlines "SLEEP_TIME_AFTER_SUDO" "$val" + sleep_after_sudo=1 + ;; + sleep | sleep=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + sleep-if-fail) + sudo_log_args "Parsing option: '--${OPTARG%=*}'" + sleep_only_on_failure=1 + ;; + sleep-if-fail*) + sudo_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + su-env-options=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + IFS=$'\n' val_arr=( $(xargs -r -n1 -- printf -- "%s\n" <<<"$val") ) + IFS=$' \t\n' + SUDO_SU_ENV_ADDITIONAL_COMMAND_OPTIONS=( "${val_arr[@]}" ) + ;; + su-env-options | su-env-options=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + su-run-options=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + IFS=$'\n' val_arr=( $(xargs -r -n1 -- printf -- "%s\n" <<<"$val") ) + IFS=$' \t\n' + SUDO_SU_RUN_ADDITIONAL_COMMAND_OPTIONS=( "${val_arr[@]}" ) + ;; + su-run-options | su-run-options=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + title=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + sudo_trim_trailing_newlines "SUDO_SHELL_TERMINAL_TITLE" "$val" + set_sudo_shell_terminal_title=1 + ;; + title | title=) + SUDO_SHELL_TERMINAL_TITLE="" + set_sudo_shell_terminal_title=1 + ;; + work-dir=?*) + val="$long_optargs" + sudo_log_args "Parsing option: '--${OPTARG%=*}', value: '${val}'" + #if parse_commands_as_per_run_command_intent_rules is enabled + if [[ "$parse_commands_as_per_run_command_intent_rules" == "1" ]]; then + sudo_replace_comma_alternative_chars_with_commas_in_string "val" "$val" + fi + sudo_trim_trailing_newlines "SUDO_SHELL_WORKING_DIR" "$val" + ;; + work-dir | work-dir=) + sudo_log_arg_errors "No parameters set for option: '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + '' ) #"--" terminates argument processing to support non-options that start with dashes + sudo_log_args "Parsing option: '--'" + break + ;; + *) + sudo_log_arg_errors "Unknown option '--${OPTARG%=*}'" + exit_sudo_on_error + ;; + esac + ;; + h) + sudo_log_args "Parsing option: '-${opt}'" + show_sudo_help + $sudo_exit_command 0 + ;; + v) + sudo_log_args "Parsing option: '-${opt}'" + if [ "$sudo_verbose_level" -lt "2" ]; then + sudo_verbose_level=$((sudo_verbose_level+1)); + else + sudo_log_arg_errors "Invalid Option, max verbose level is 2" + exit_sudo_on_error + fi + ;; + a) + sudo_log_args "Parsing option: '-${opt}'" + force_set_priority_to_android_paths=1 + ;; + b) + sudo_log_args "Parsing option: '-${opt}'" + go_back_to_last_activity_after_running_core_script=1 + ;; + B) + sudo_log_args "Parsing option: '-${opt}'" + run_core_script_in_background=1 + ;; + c) + sudo_log_args "Parsing option: '-${opt}'" + clear_shell_after_running_core_script=1 + ;; + d) + sudo_log_args "Parsing option: '-${opt}'" + disable_stdin_for_core_script=1 + ;; + D) + sudo_log_args "Parsing option: '-${opt}'" + disable_preserve_environment_for_su=1 + ;; + e) + sudo_log_args "Parsing option: '-${opt}'" + exit_early_if_core_script_fails=1 + ;; + E) + sudo_log_args "Parsing option: '-${opt}'" + exec_sudo_shell=1 + ;; + f) + sudo_log_args "Parsing option: '-${opt}'" + force_use_temp_script_file_for_core_script=1 + ;; + F) + sudo_log_args "Parsing option: '-${opt}'" + core_script_is_path_to_script_file=1 + ;; + H) + sudo_log_args "Parsing option: '-${opt}'" + same_sudo_post_shell_home_as_sudo_shell_home=1 + ;; + i) + sudo_log_args "Parsing option: '-${opt}'" + run_interactive_post_sudo_shell_after_running_core_script=1 + ;; + l) + sudo_log_args "Parsing option: '-${opt}'" + go_to_launcher_activity_after_running_core_script=1 + ;; + L) + sudo_log_args "Parsing option: '-${opt}'" + export_all_existing_paths_in_ld_library_path_variable=1 + ;; + n) + sudo_log_args "Parsing option: '-${opt}'" + SUDO_CORE_SCRIPT_REDIRECT_MODE=3 + ;; + N) + sudo_log_args "Parsing option: '-${opt}'" + SUDO_CORE_SCRIPT_REDIRECT_MODE=4 + ;; + o) + sudo_log_args "Parsing option: '-${opt}'" + SUDO_CORE_SCRIPT_REDIRECT_MODE=0 + ;; + O) + sudo_log_args "Parsing option: '-${opt}'" + SUDO_CORE_SCRIPT_REDIRECT_MODE=1 + ;; + p) + sudo_log_args "Parsing option: '-${opt}'" + command_type="path" + command_type_path_forced=1 + ;; + P) + sudo_log_args "Parsing option: '-${opt}'" + export_all_existing_paths_in_path_variable=1 + ;; + r) + sudo_log_args "Parsing option: '-${opt}'" + parse_commands_as_per_run_command_intent_rules=1 + ;; + R) + sudo_log_args "Parsing option: '-${opt}'" + use_root_for_path_search_and_validation=1 + ;; + s) + sudo_log_args "Parsing option: '-${opt}'" + command_type="script" + ;; + S) + sudo_log_args "Parsing option: '-${opt}'" + same_sudo_post_shell_as_sudo_shell=1 + ;; + \?) + sudo_log_arg_errors "Unknown option: '-${OPTARG}'" + exit_sudo_on_error + ;; + esac + done + shift $((OPTIND -1)) #remove already processed arguments from argument list + + #if arguments received + if [ $# -ne 0 ]; then + #if (command_type_path_forced is not enabled and $1 equals "su" or "asu" + if [[ "$command_type_path_forced" != "1" ]] && ([[ "$1" == "su" ]] || [[ "$1" == "asu" ]]); then + command_type="$1" + shift #remove $1 from the argument list + + #if more arguments are given + if [ $# -ne 0 ]; then + sudo_log_arg_errors "Unknown arguments to \"$command_type\": '$*'" + exit_sudo_on_error + fi + #if command_type equals "path" or "script" + elif [[ "$command_type" == "path" ]] || [[ "$command_type" == "script" ]]; then + #set rest of the arguments to the sudo script as SUDO_COMMAND + SUDO_COMMAND=() + local return_value + local read_argument_from_fd + local fd_number + local i=1 + for arg in "$@"; do + read_argument_from_fd=0 + + #if process substitution was used to pass path to an fd file as argument that contains the actual argument value + #and the fd is not of stdin, stdout or stderr + if [[ "$arg" =~ ^/proc/self/fd/[0-9]+$ ]] && [[ ! "$arg" =~ ^/proc/self/fd/(0|1|2)$ ]]; then + fd_number="${arg##*/}" + #if fd is readable, then + if read -t 0 -u "$fd_number" 2>/dev/null; then + #enable read_argument_from_fd + read_argument_from_fd=1 + + #if command_type equals "script" and its the first argument + if [[ "$command_type" == "script" ]] && [ $i -eq 1 ]; then + #disable read_argument_from_fd since we are reading it here with cat + #fd can't be read again, seeking backwards is not possible + read_argument_from_fd=0 + + #read core_script from fd in encoded form since it may contain non UTF-8 or binary data + encoded_arg="$(cat "$arg" | base64)" + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 1 ]; then + sudo_log_arg_errors "Failure while encoding core_script received as argument" + $sudo_exit_command $return_value + fi + + #check if core_script contains non UTF-8 or binary data + #decode data first before passing to grep + printf '%s' "$encoded_arg" | base64 -d | grep -axvq '.*' + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 1 ]; then + sudo_log_arg_errors "Failure while checking if core_script contains non UTF-8 or binary data" + $sudo_exit_command $return_value + #if found + elif [ $return_value -eq 0 ]; then + #enable use of temp script file for core_script and enable decoding + force_use_temp_script_file_for_core_script=1 + decode_core_script_content=1 + + #set encoded data to arg + arg="$encoded_arg" + #if not found + else + #set decoded data to arg + arg="$(printf '%s' "$encoded_arg" | base64 -d)" + fi + fi + fi + fi + + #if read_argument_from_fd is enabled + if [[ "$read_argument_from_fd" == "1" ]]; then + #use the fd file content as argument value + SUDO_COMMAND+=( "$(<"$arg")" ) + else + #directly use the argument value + SUDO_COMMAND+=("$arg") + fi + + i=$((i + 1)) + done + shift $# #remove all remaining arguments from argument list + fi + #else show help + else + if [[ "$command_type" == "script" ]]; then + SUDO_COMMAND=() + else + show_sudo_help + $sudo_exit_command 0 + fi + fi + +} + +show_sudo_help() { + + cat <<'SUDO_HELP_EOF' + +sudo is a wrapper script to drop to the supported shells or execute +shell script files or their text passed as an argument with super +user (root) context in termux. + + +Usage: + sudo [command_options] su + sudo [command_options] asu + sudo [command_options] [-p] <command> [command_args] + sudo [command_options] -s <core_script> [core_script_args] + + +Available command_options: + [ -h | --help ] display this help screen + [ --help-extra ] display more help about how sudo command works + [ --version ] display version + [ -v | -vv ] set verbose level to 1 or 2 + [ -a ] force set priority to android paths for path + command type + [ -b ] go back to last activity after running core_script + [ -B ] run core_script in background + [ -c ] clear shell after running core_script + [ -d ] disable stdin for core_script + [ -D ] disable preserve environment for su + [ -e ] exit early if core_script fails + [ -E ] exec interactive shell or the path command + [ -f ] force use temp script file for core_script + [ -F ] consider core_script to be a path to script file + instead of script text + [ -H ] same sudo post shell home as sudo shell home + [ -i ] run interactive sudo post shell after running + core_script + [ -l ] go to launcher activtiy after running core_script + [ -L ] export all existing paths in '$LD_LIBRARY_PATH' + variable + [ -n ] redirect stderr to /dev/null for core_script + [ -N ] redirect stdout and stderr to /dev/null for + core_script + [ -o ] redirect stderr to stdout for core_script + [ -O ] redirect stdout to stderr for core_script + [ -p ] set 'path' as command type [default] + [ -P ] export all existing paths in '$PATH' variable + [ -r ] parse commands as per RUN_COMMAND intent rules + [ -R ] use root for searching and validating paths + [ -s ] set 'script' as command type + [ -S ] same sudo post shell as sudo shell + [ --comma-alternative=<alternative> ] + comma alternative character to be used for + the `-r` option instead of the default + [ --dry-run ] + do not execute sudo commands + [ --export-paths=<paths> ] + additional paths to export in PATH variable, + separated with colons ':' + [ --export-ld-lib-paths=<paths> ] + additional paths to export in LD_LIBRARY_PATH + variable, separated with colons ':' + [ --force-remount-ro ] + force remount rootfs and system partitions back + to ro after sudo commands + [ --hold[=<string>] ] + hold sudo from exiting until string is entered, + defaults to any character if string is not passed + [ --hold-if-fail ] + if '--hold' option is passed, then only hold if + exit code of sudo does not equal '0' + [ --list-interactive-shells ] + display list of supported interactive shells + [ --list-script-shells ] + display list of supported script shells + [ --no-create-rc ] + do not create rc files automatically + [ --no-create-hist ] + do not create history files automatically + [ --no-hist ] + do not save history for sudo shell and sudo post + shell + [ --no-log-args ] + do not log arguments and core_script content + when verbose mode is enabled + [ --no-remount-ro ] + do not remount rootfs and system partitions back + to ro after sudo commands + [ --keep-temp ] + do not delete sudo temp directory on exit + [ --post-shell=<shell> ] + name or absolute path for sudo post shell + [ --post-shell-home=<path> ] + absolute path for sudo post shell home + [ --post-shell-options=<options> ] + additional options to pass to sudo post shell + [ --post-shell-post-commands=<commands> ] + bash commands to run after sudo post shell + [ --post-shell-pre-commands=<commands> ] + bash commands to run before sudo post shell + [ --post-shell-stdin-string=<string> ] + string to pass as stdin to sudo post shell + [ --remove-prev-temp ] + remove temp files and directories created on + previous runs of sudo command + [ --script-decode ] + consider the core_script as base64 + encoded that should be decoded before execution + [ --script-name=<name> ] + filename to use for the core_script temp file + created in '.sudo.temp.XXXXXX' directory instead + of 'sudo_core_script' + [ --script-redirect=<mode/string> ] + core_script redirect mode for stdout and stderr + [ --shell=<shell> ] + name or absolute path for sudo shell + [ --shell-home=<path> ] + absolute path for sudo shell home + [ --shell-options=<options> ] + additional options to pass to sudo shell + [ --shell-post-commands=<commands> ] + bash commands to run after sudo shell for script + command type + [ --shell-pre-commands=<commands> ] + bash commands to run before sudo shell + [ --shell-stdin-string=<string> ] + string to pass as stdin to sudo shell for script + command type + [ --sleep=<seconds> ] + sleep for x seconds before exiting sudo + [ --sleep-if-fail ] + if '--sleep' option is passed, then only sleep if + exit code of sudo does not equal '0' + [ --su-env-options=<options> ] + additional options to pass to su that sets up the + sudo environment + [ --su-run-options=<options> ] + additional options to pass to su that runs the + final sudo command_type command + [ --title=<title> ] + title for sudo shell terminal + [ --work-dir=<path> ] + absolute path for working directory + + +Set verbose level to 1 or 2 to get more info when running sudo command. + +Pass '--dry-run' option with verbose mode enabled to see the commands +that will be run without actually executing them. + +Visit https://github.com/agnostic-apollo/sudo for more help on how +sudo command works. +SUDO_HELP_EOF + +echo $'\n'"Supported interactive shells: \`$SUDO_SUPPORTED_INTERACTIVE_SHELLS\`" +echo $'\n'"Supported script shells: \`$SUDO_SUPPORTED_SCRIPT_SHELLS\`" + +} + +show_sudo_help_extra() { + + show_sudo_help + + cat <<'SUDO_HELP_EOF' + + +The 'su' command type drops to an interactive shell in superuser (root) +context for any of the supported interactive shells. To drop to a root +'bash' shell, just run 'sudo su'. The priority will be set to termux +bin and library paths in '$PATH' and '$LD_LIBRARY_PATH' variables. +Use the '--shell' option to set the interactive shell to use. + + +The 'asu' command type is the same as 'su' command type but +instead the priority will be set to android bin and library paths in +'$PATH' and '$LD_LIBRARY_PATH' variables. +Use the '--shell' option to set the interactive shell to use. + + +The 'path' command type runs a single command in superuser (root) +context. You can use it just by running 'sudo <command> [command_args]' +where 'command' is the executable you want to run and 'command_args' +are any optional arguments to it. The 'command' will be run within a +'bash' shell. Priority is given to termux bin and library paths unless +'command' exists in '/system' partition. +To call the 'su' binary, run the 'sudo -p su [user]' command. + + +The 'script' command type takes any script text or path to a script +file for any of the supported script shells referred as 'sudo shell', +and executes the script with any optional arguments with the desired +script shell. This can be done by running the +'sudo -s <core_script> [core_script_args]' command. +The 'core_script' will be considered a 'bash' script by default. +The 'core_script' will be passed to the desired shell using +process substitution or after storing the 'core_script' in a temp file +in a temp directory in 'sudo shell' home +'$HOME/.sudo.temp.XXXXXX/sudo_core_script' and passing the path to +the desired shell, where 'XXXXXX' is a randomly generated string. +The method is automatically chosen based on the script shell +capabilities. The '-f' option can be used to force the usage of a +script file. The '-F' option can passed so that the 'core_script' +is considered as a path to script file that should be passed to +'sudo shell' directly instead of considering it as a script text. +Use the '--shell' option to set the script shell to use. +Use the '--post-shell' option to set the interactive shell to use if +'-i' option is passed. + + +Run "exit" command of your shell to exit interactive shells and return +to the termux shell. +SUDO_HELP_EOF + +} + +exit_sudo_on_error() { + + show_sudo_help + sudo_run_pre_exit_commands 1 + $sudo_exit_command 1 + +} + +#call sudo_main function +[[ x"${BASH_SOURCE[0]}" == x"$0" ]] && sudo_main "$@"; $sudo_exit_command 0; diff --git a/sudo.config b/sudo.config new file mode 100755 index 0000000..7176a5d --- /dev/null +++ b/sudo.config @@ -0,0 +1,50 @@ +#.sudo.config + +TERMUX_FILES="/data/data/com.termux/files" +TERMUX_HOME="$TERMUX_FILES/home" +TERMUX_PREFIX="$TERMUX_FILES/usr" + +#set SUDO_SHELL_HOME which will be used as sudo shell home directory +#SUDO_SHELL_HOME="$TERMUX_HOME/.suroot" #default to '$TERMUX_HOME/.suroot' +#SUDO_SHELL_HOME="$TERMUX_HOME" #use '$TERMUX_HOME' as sudo shell home + +#set SUDO_POST_SHELL_HOME which will be used as sudo post shell home directory +#SUDO_POST_SHELL_HOME="$TERMUX_HOME/.suroot" #default to '$TERMUX_HOME/.suroot' +#SUDO_POST_SHELL_HOME="$TERMUX_HOME" #use '$TERMUX_HOME' as sudo post shell home + +#set the prompt string 1 for the sudo shell +#SUDO_SHELL_PS1="# " #default to "# " + +#set the prompt string 1 for the sudo post shell +#SUDO_POST_SHELL_PS1="# " #default to "# " + +#set any additional paths you want to export other than termux bin, android bin and su bin paths, +#separated with colons `:` +#the string must not start or end with or contain two consecutive colons ':' +#the `--export-paths` option will override this value +#ADDITIONAL_PATHS_TO_EXPORT="" + +#set to "1" or pass "-L" as argument if you want to automatically export all the paths that already exist +#in the PATH variable at the moment the sudo command is run +#the default paths mentioned above and any path in ADDITIONAL_PATHS_TO_EXPORT are always exported +#export_all_existing_paths_in_path_variable=0 #default to 0 + +#set any additional ld library paths you want to export other than termux lib and android lib paths, +#separated with colons `:` +#the string must not start or end with or contain two consecutive colons ':' +#the `--export-ld-lib-paths` option will override this value +#ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT="" + +#set to "1" or pass "-P" as argument if you want to automatically export all the paths that already exist +#in the LD_LIBRARY_PATH variable at the moment the sudo command is run +#the default paths mentioned above and any path in ADDITIONAL_LD_LIBRARY_PATHS_TO_EXPORT are always exported +#export_all_existing_paths_in_ld_library_path_variable=0 #default to 0 + +#set to 1 if you want to automatically create rc files for interactive shells, otherwise 0 +#sudo_shells_automatically_create_rc_files=1 #default to 1 + +#set to 1 if you want to automatically create history files for interactive shells, otherwise 0 +#sudo_shells_automatically_create_history_files=1 #default to 1 + +#set to 1 if you want to store command history for interactive shells, otherwise 0 +#sudo_shells_history_enabled=1 #default to 1 diff --git a/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.md b/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.md new file mode 100755 index 0000000..8d2c91c --- /dev/null +++ b/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.md @@ -0,0 +1,716 @@ +# Termux RUN COMMAND Intent Sudo Templates + +  +## Export Info: +**Tasker Version:** `5.11.7.beta` +**Timestamp:** `2020-12-14 15.54.33` +  + + + +  +## Profile Names: +**Count:** `0` + + + + +## Scene Names: +**Count:** `0` + + + + +## Task Names: +**Count:** `1` + +- `Termux RUN_COMMAND Intent Sudo Templates` +## +  + + + +  +## Profiles Info: +  + + + +  +## Tasks Info: +  + +### Task 1 +**Name:** `Termux RUN_COMMAND Intent Sudo Templates` +**ID:** `999` +**Collision Handling:** `Abort New Task` +**Keep Device Awake:** `false` + +#### Help: + +A task that provides templates for running `sudo` script commands with the `RUN_COMMAND` intent in `superuser (root)` context in termux. The commands are run using the Tasker `TermuxCommand()` function of the `Tasker Function` action and with the `am` command with the `Run Shell` action and are referred as intent actions in this task. This task requires Termux:Tasker version `>= 0.5`. Tasker must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file, otherwise any commands received via the `RUN_COMMAND` intent will not be executed by Termux. For android `>= 10`, Termux must also be granted `Draw Over Apps` permissions so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like `SuperSU` or `Magisk` for the `sudo` script to work. + +Check [Termux:Tasker Github](https://github.com/termux/termux-tasker) and [RunCommand Intent](https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/RunCommandService.java) for more details on `RUN_COMMAND` intent configuration. + +Check [sudo](https://github.com/agnostic-apollo/sudo) for more details for the `sudo` script. + + +The result of commands is not received back when commands are run with the `RUN_COMMAND` intent, hence all templates run their commands in the foreground terminal session so that the results can be viewed. The `--sleep=3` command option is also passed to `sudo` so that it sleeps for `3` seconds before exiting. If this is not passed, the terminal would immediately close after `sudo` executes its commands without giving the user a chance to view the results in the terminal session. Moreover, a `Wait` action of `2` seconds is also added to the task after each template so that the templates execute in order and don't start until the previous one has at least ideally started executing. + +The default value of the `%comma_alternative` variable in this task is set to `‚` (`#U+201A`, `‚`, `‚`, `single low-9 quotation mark`). This is the same character that is replaced with the simple comma `,` (`U+002C`, `,`, `,`, `comma`) by the `sudo` script by default when the `-r` option is passed. You can use a different character that should be replaced using the `--comma-alternative` option. + + +Template 1 runs the `$PREFIX/bin/sudo --sleep=3 dumpsys -l` command in the foreground using `TermuxCommand()` function as a template for the `path` `command_type` to list all android services. The arguments do not contain any simple comma characters and hence can be passed directly without having to replace them with `%comma_alternative` characters. + + +Template 2 runs the `$PREFIX/bin/sudo -sr --sleep=3 '%core_script' '%argument_1' '%argument_2'` command in the foreground using `TermuxCommand()` function as a template for the `script` `command_type` with `bash` as the `sudo shell`, which would be chosen automatically by default without having to pass the `--shell` option. The `termux_tasker_basic_bash_test` `bash` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain simple comma characters. The `-s` command option is passed to set `command_type` to `script`. The `-r` command option is passed so that `sudo` parses the arguments as per `RUN_COMMAND` intent rules. The `sudo` executable is passed to the intent using the `%executable` variable. The `%core_script`, `%argument_1` and `%argument_2` variables are used to store the dynamic values to be sent as `$1`, `$2` and `$3` respectively to `sudo`. They are first all set in the `%arguments` variable separated by simple comma characters, which is passed to the intent. The `%core_script`, `%argument_1` and `%argument_2` variables may contain any type of characters, even a simple comma, but simple commas must be replaced with the `%comma_alternative` characters before the intent action is run so that their values are not split into multiple arguments by the intent. To replace the simple commas, `Variable Search Replace` action is run to replace all simple commas `,` with the `%comma_alternative` variable value. The `Variable Search Replace` action must be used separately for each argument variable before adding it to the `%arguments` variable. Do not set multiple arguments in the same variable and use `Variable Search Replace` action on it since that will result in incorrect argument splitting. This template shows how you can dynamically create intent commands at runtime using variables and send them via the `RUN_COMMAND` intent to Termux for execution, including passing the script text itself without having to create a physical file in `~/.termux/tasker/` directory. + + +Template 3 is almost the same as Template 2, but it runs the `$PREFIX/bin/sudo -sr --shell=python --sleep=3 '%core_script' '%argument_1' '%argument_2'` command in the foreground using `am` command as a template for the `script` `command_type` with `python` as the `sudo shell`. The `termux_tasker_basic_python_test` `python` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain simple comma characters. However, as already mentioned that this template uses the `am` command, the `%arguments` variable is passed to the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra surrounded with single quotes inside a shell and hence any single quotes inside the `%arguments` variable also need to be escaped before running the intent command to prevent incorrect quoting. To escape the single quotes, `Variable Search Replace` action is run to replace all single quotes `'` with one single quote, followed by one backslash, followed by two single quotes `'\''`. So `%arguments` surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed as the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra value. The `Variable Search Replace` action does not need to be used separately for each argument variable before adding it to the `%arguments` variable, running it on only the final `%arguments` variable would be fine, unlike how its done on individual argument variables like `%argument_1`, etc for usage with `Termux:Tasker` plugin. + + +Template 4 runs `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` in the foreground using `TermuxCommand()` function as a template for the `su` `command_type` to start an interactive `bash` shell with priority to termux binaries and libraries. The `--shell-pre-commands` option is passed to run some commands before starting the interactive `bash` shell and its argument is **not** passed surrounded with single or double quotes to prevent whitespace splitting in the intent action, like done for usage with `Termux:Tasker` plugin since splitting will occur on simple comma characters instead. If there are going to be simple comma characters in arguments to the command options or in the main arguments to `sudo`, you must replace them with `%comma_alternative` variable value and pass the `-r` option. The `--title` option is passed to set the title of the terminal. + + +The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. These shortcuts can be used in any path arguments to the `sudo` command. + + +The `%command_failed` variable will be set if the intent action failed, this is detected by whether `%err` or `%errmsg` is set by the intent action. If you run multiple intent actions in the same task or are using `Local Variable Passthrough`, then you must clear the `%command_failed` variable and optionally the `%errmsg` variable with the `Variable Clear` action before running each intent action, in case they were already set, like by a previously failed intent action after which the task was not stopped. +## + + +**Parameters:** `-` + + +**Returns:** `-` + + +**Control:** + +``` +version_name: 0.1.0 +``` +## +  + + + +  +## Code Description: +  + +`````` +Task Name: Termux RUN_COMMAND Intent Sudo Templates + +Actions: + <A task that provides templates for running `sudo` script commands with the `RUN_COMMAND` intent in `superuser (root)` context in termux. The commands are run using the Tasker `TermuxCommand()` function of the `Tasker Function` action and with the `am` command with the `Run Shell` action and are referred as intent actions in this task. This task requires Termux:Tasker version `>= 0.5`. Tasker must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file, otherwise any commands received via the `RUN_COMMAND` intent will not be executed by Termux. For android `>= 10`, Termux must also be granted `Draw Over Apps` permissions so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like `SuperSU` or `Magisk` for the `sudo` script to work. + + Check [Termux:Tasker Github](https://github.com/termux/termux-tasker) and [RunCommand Intent](https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/RunCommandService.java) for more details on `RUN_COMMAND` intent configuration. + + Check [sudo](https://github.com/agnostic-apollo/sudo) for more details for the `sudo` script. + + + The result of commands is not received back when commands are run with the `RUN_COMMAND` intent, hence all templates run their commands in the foreground terminal session so that the results can be viewed. The `--sleep=3` command option is also passed to `sudo` so that it sleeps for `3` seconds before exiting. If this is not passed, the terminal would immediately close after `sudo` executes its commands without giving the user a chance to view the results in the terminal session. Moreover, a `Wait` action of `2` seconds is also added to the task after each template so that the templates execute in order and don't start until the previous one has at least ideally started executing. + + The default value of the `%comma_alternative` variable in this task is set to `‚` (`#U+201A`, `‚`, `‚`, `single low-9 quotation mark`). This is the same character that is replaced with the simple comma `,` (`U+002C`, `,`, `,`, `comma`) by the `sudo` script by default when the `-r` option is passed. You can use a different character that should be replaced using the `--comma-alternative` option. + + + Template 1 runs the `$PREFIX/bin/sudo --sleep=3 dumpsys -l` command in the foreground using `TermuxCommand()` function as a template for the `path` `command_type` to list all android services. The arguments do not contain any simple comma characters and hence can be passed directly without having to replace them with `%comma_alternative` characters. + + + Template 2 runs the `$PREFIX/bin/sudo -sr --sleep=3 '%core_script' '%argument_1' '%argument_2'` command in the foreground using `TermuxCommand()` function as a template for the `script` `command_type` with `bash` as the `sudo shell`, which would be chosen automatically by default without having to pass the `--shell` option. The `termux_tasker_basic_bash_test` `bash` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain simple comma characters. The `-s` command option is passed to set `command_type` to `script`. The `-r` command option is passed so that `sudo` parses the arguments as per `RUN_COMMAND` intent rules. The `sudo` executable is passed to the intent using the `%executable` variable. The `%core_script`, `%argument_1` and `%argument_2` variables are used to store the dynamic values to be sent as `$1`, `$2` and `$3` respectively to `sudo`. They are first all set in the `%arguments` variable separated by simple comma characters, which is passed to the intent. The `%core_script`, `%argument_1` and `%argument_2` variables may contain any type of characters, even a simple comma, but simple commas must be replaced with the `%comma_alternative` characters before the intent action is run so that their values are not split into multiple arguments by the intent. To replace the simple commas, `Variable Search Replace` action is run to replace all simple commas `,` with the `%comma_alternative` variable value. The `Variable Search Replace` action must be used separately for each argument variable before adding it to the `%arguments` variable. Do not set multiple arguments in the same variable and use `Variable Search Replace` action on it since that will result in incorrect argument splitting. This template shows how you can dynamically create intent commands at runtime using variables and send them via the `RUN_COMMAND` intent to Termux for execution, including passing the script text itself without having to create a physical file in `~/.termux/tasker/` directory. + + + Template 3 is almost the same as Template 2, but it runs the `$PREFIX/bin/sudo -sr --shell=python --sleep=3 '%core_script' '%argument_1' '%argument_2'` command in the foreground using `am` command as a template for the `script` `command_type` with `python` as the `sudo shell`. The `termux_tasker_basic_python_test` `python` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain simple comma characters. However, as already mentioned that this template uses the `am` command, the `%arguments` variable is passed to the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra surrounded with single quotes inside a shell and hence any single quotes inside the `%arguments` variable also need to be escaped before running the intent command to prevent incorrect quoting. To escape the single quotes, `Variable Search Replace` action is run to replace all single quotes `'` with one single quote, followed by one backslash, followed by two single quotes `'\''`. So `%arguments` surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed as the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra value. The `Variable Search Replace` action does not need to be used separately for each argument variable before adding it to the `%arguments` variable, running it on only the final `%arguments` variable would be fine, unlike how its done on individual argument variables like `%argument_1`, etc for usage with `Termux:Tasker` plugin. + + + Template 4 runs `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` in the foreground using `TermuxCommand()` function as a template for the `su` `command_type` to start an interactive `bash` shell with priority to termux binaries and libraries. The `--shell-pre-commands` option is passed to run some commands before starting the interactive `bash` shell and its argument is **not** passed surrounded with single or double quotes to prevent whitespace splitting in the intent action, like done for usage with `Termux:Tasker` plugin since splitting will occur on simple comma characters instead. If there are going to be simple comma characters in arguments to the command options or in the main arguments to `sudo`, you must replace them with `%comma_alternative` variable value and pass the `-r` option. The `--title` option is passed to set the title of the terminal. + + + The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. These shortcuts can be used in any path arguments to the `sudo` command. + + + The `%command_failed` variable will be set if the intent action failed, this is detected by whether `%err` or `%errmsg` is set by the intent action. If you run multiple intent actions in the same task or are using `Local Variable Passthrough`, then you must clear the `%command_failed` variable and optionally the `%errmsg` variable with the `Variable Clear` action before running each intent action, in case they were already set, like by a previously failed intent action after which the task was not stopped. + ## + + + **Parameters:** `-` + + + **Returns:** `-` + + + **Control:** + + ``` + version_name: 0.1.0 + ```> + A1: Anchor + + A2: Variable Set [ + Name:%task_name + To:Termux RUN_COMMAND Intent Sudo Templates + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <Template 1 Start> + A3: Anchor + + <Goto "Template 2 Start" + Enable this action to skip running this template> + A4: [X] Goto [ + Type:Action Label + Number:1 + Label:Template 2 Start ] + + <Run `$PREFIX/bin/sudo --sleep=3 dumpsys -l` Command In Foreground> + A5: Anchor + + A6: Variable Clear [ + Name:%command_failed/%errmsg + Pattern Matching:On + Local Variables Only:On + Clear All Variables:Off ] + + <Run Termux RUN_COMMAND Intent Command with TermuxCommand() Function> + A7: Tasker Function [ + Function:TermuxCommand($PREFIX/bin/sudo,--sleep=3,dumpsys,-l,/data/data/com.termux/files/home,false) Continue Task After Error:On ] + + A8: Variable Set [ + Name:%command_failed + To:err = `%err` + + errmsg = + ``` + %errmsg + ``` + + stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] If [ %err Set | %errmsg Set ] + + A9: If [ %command_failed Set ] + + <remove %err and %errmsg if not set> + A10: Variable Search Replace [ + Variable:%command_failed + Search:^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+ + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With: Continue Task After Error:On ] If [ %command_failed Set ] + + A11: Text Dialog [ + Title:Template 1 Command + Failed + Text:%command_failed + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A12: Stop [ + With Error:Off + Task: ] + + A13: End If + + <Template 1 End> + A14: Anchor + + A15: Wait [ + MS:0 + Seconds:2 + Minutes:0 + Hours:0 + Days:0 ] + + <Template 2 Start> + A16: Anchor + + <Goto "Template 3 Start" + Enable this action to skip running this template> + A17: [X] Goto [ + Type:Action Label + Number:1 + Label:Template 3 Start ] + + <Run `$PREFIX/bin/sudo -sr --sleep=3 '%core_script' '%argument_1' '%argument_2'` Command In Foreground> + A18: Anchor + + <set `‚` (`#U+201A`, `‚`, `‚`, `single low-9 quotation mark`) to %comma_alternative> + A19: Variable Set [ + Name:%comma_alternative + To:‚ + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <set `$PREFIX/bin/sudo` to %executable> + A20: Variable Set [ + Name:%executable + To:$PREFIX/bin/sudo + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <set `termux_tasker_basic_bash_test` script text to %core_script> + A21: Variable Set [ + Name:%core_script + To:#if parameter count is not 2 + if [ $# -ne 2 ]; then + echo "Invalid parameter count '$#' to 'termux_tasker_basic_bash_test'" 1>&2 + echo "$*" 1>&2 + exit 1 + fi + + echo "\$1=\`$1\`" + echo "\$2=\`$2\`" + + exit 0 + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A22: Variable Set [ + Name:%argument_1 + To:json + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A23: Variable Set [ + Name:%argument_2 + To:{ + "name":"I'm Termux", + "license":"GPLv3", + "addons": { + "1":"Termux:API", + "2":"Termux:Boot", + "3":"Termux:Float", + "4":"Termux:Styling", + "5":"Termux:Tasker", + "6":"Termux:Widget" + } + } + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <replace all simple commas `,` (`U+002C`, `,`, `,`, `comma`) with %comma_alternative characters> + A24: Variable Search Replace [ + Variable:%core_script + Search:, + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:%comma_alternative ] If [ %core_script Set ] + + <replace all simple commas `,` (`U+002C`, `,`, `,`, `comma`) with %comma_alternative characters> + A25: Variable Search Replace [ + Variable:%argument_1 + Search:, + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:%comma_alternative ] If [ %argument_1 Set ] + + <replace all simple commas `,` (`U+002C`, `,`, `,`, `comma`) with %comma_alternative characters> + A26: Variable Search Replace [ + Variable:%argument_2 + Search:, + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:%comma_alternative ] If [ %argument_2 Set ] + + <set `-sr,--sleep=3,%core_script,%argument_1,%argument_2` to %arguments> + A27: Variable Set [ + Name:%arguments + To:-sr,--sleep=3,%core_script,%argument_1,%argument_2 + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <set `%executable,%arguments,/data/data/com.termux/files/home,false` to %termux_command> + A28: Variable Set [ + Name:%termux_command + To:%executable,%arguments,/data/data/com.termux/files/home,false + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A29: Variable Clear [ + Name:%command_failed/%errmsg + Pattern Matching:On + Local Variables Only:On + Clear All Variables:Off ] + + <Run Termux RUN_COMMAND Intent Command with TermuxCommand() Function> + A30: Tasker Function [ + Function:TermuxCommand(%termux_command) Continue Task After Error:On ] + + A31: Variable Set [ + Name:%command_failed + To:err = `%err` + + errmsg = + ``` + %errmsg + ``` + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] If [ %err Set | %errmsg Set ] + + A32: If [ %command_failed Set ] + + A33: Text Dialog [ + Title:Template 2 Command + Failed + Text:%command_failed + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A34: Stop [ + With Error:Off + Task: ] + + A35: End If + + <Template 2 End> + A36: Anchor + + A37: Wait [ + MS:0 + Seconds:2 + Minutes:0 + Hours:0 + Days:0 ] + + <Template 3 Start> + A38: Anchor + + <Goto "Template 4 Start" + Enable this action to skip running this template> + A39: [X] Goto [ + Type:Action Label + Number:1 + Label:Template 4 Start ] + + <Run `$PREFIX/bin/sudo -sr --shell=python --sleep=3 '%core_script' '%argument_1' '%argument_2'` Command In Foreground> + A40: Anchor + + <set `‚` (`#U+201A`, `‚`, `‚`, `single low-9 quotation mark`) to %comma_alternative> + A41: Variable Set [ + Name:%comma_alternative + To:‚ + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <set `$PREFIX/bin/sudo` to %executable> + A42: Variable Set [ + Name:%executable + To:$PREFIX/bin/sudo + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <set `termux_tasker_basic_python_test` script text to %core_script> + A43: Variable Set [ + Name:%core_script + To:import sys + + argv_size = len(sys.argv) - 1 + + # if parameter count is not 2 + if argv_size != 2: + print("Invalid parameter count '%s' to 'termux_tasker_basic_python_test'" % argv_size, file=sys.stderr) + print("%s" % " ".join(sys.argv[1:]), file=sys.stderr) + sys.exit(1) + + print("$1=`%s`" % sys.argv[1]) + print("$2=`%s`" % sys.argv[2]) + + sys.exit(0) + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A44: Variable Set [ + Name:%argument_1 + To:json + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A45: Variable Set [ + Name:%argument_2 + To:{ + "name":"I'm Termux", + "license":"GPLv3", + "addons": { + "1":"Termux:API", + "2":"Termux:Boot", + "3":"Termux:Float", + "4":"Termux:Styling", + "5":"Termux:Tasker", + "6":"Termux:Widget" + } + } + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <replace all simple commas `,` (`U+002C`, `,`, `,`, `comma`) with %comma_alternative characters> + A46: Variable Search Replace [ + Variable:%core_script + Search:, + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:%comma_alternative ] If [ %core_script Set ] + + <replace all simple commas `,` (`U+002C`, `,`, `,`, `comma`) with %comma_alternative characters> + A47: Variable Search Replace [ + Variable:%argument_1 + Search:, + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:%comma_alternative ] If [ %argument_1 Set ] + + <replace all simple commas `,` (`U+002C`, `,`, `,`, `comma`) with %comma_alternative characters> + A48: Variable Search Replace [ + Variable:%argument_2 + Search:, + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:%comma_alternative ] If [ %argument_2 Set ] + + <set `-sr,--shell=python,--sleep=3,%core_script,%argument_1,%argument_2` to %arguments> + A49: Variable Set [ + Name:%arguments + To:-sr,--shell=python,--sleep=3,%core_script,%argument_1,%argument_2 + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <replace all single quotes (') with ('\'')> + A50: Variable Search Replace [ + Variable:%arguments + Search:' + Ignore Case:Off + Multi-Line:On + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:'\\'' ] If [ %arguments Set ] + + A51: Variable Clear [ + Name:%command_failed/%errmsg + Pattern Matching:On + Local Variables Only:On + Clear All Variables:Off ] + + <Run Termux RUN_COMMAND Intent Command with `am` Command> + A52: Run Shell [ + Command:am startservice --user 0 -n com.termux/com.termux.app.RunCommandService -a com.termux.RUN_COMMAND --es com.termux.RUN_COMMAND_PATH '%executable' --esa com.termux.RUN_COMMAND_ARGUMENTS '%arguments' --es com.termux.RUN_COMMAND_WORKDIR '/data/data/com.termux/files/home' --ez com.termux.RUN_COMMAND_BACKGROUND 'false' + Timeout (Seconds):0 + Use Root:Off + Store Output In:%stdout + Store Errors In:%stderr + Store Result In: Continue Task After Error:On ] + + A53: Variable Set [ + Name:%command_failed + To:err = `%err` + + errmsg = + ``` + %errmsg + ``` + + stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] If [ %err Set | %errmsg Set ] + + A54: If [ %command_failed Set ] + + <remove %err and %errmsg if not set> + A55: Variable Search Replace [ + Variable:%command_failed + Search:^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+ + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With: Continue Task After Error:On ] If [ %command_failed Set ] + + A56: Text Dialog [ + Title:Template 3 Command + Failed + Text:%command_failed + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A57: Stop [ + With Error:Off + Task: ] + + A58: Else + + A59: [X] Text Dialog [ + Title:Template 3 Command Result + Text:stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A60: End If + + <Template 3 End> + A61: Anchor + + A62: Wait [ + MS:0 + Seconds:2 + Minutes:0 + Hours:0 + Days:0 ] + + <Template 4 Start> + A63: Anchor + + <Goto "Return" + Enable this action to skip running this template> + A64: [X] Goto [ + Type:Action Label + Number:1 + Label:Return ] + + <Run `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` Command In Foreground> + A65: Anchor + + A66: Variable Clear [ + Name:%command_failed/%errmsg + Pattern Matching:On + Local Variables Only:On + Clear All Variables:Off ] + + <Run Termux RUN_COMMAND Intent Command with TermuxCommand() Function> + A67: Tasker Function [ + Function:TermuxCommand($PREFIX/bin/sudo,--shell-pre-commands=echo 'starting sudo shell';,--title='sudo',su,/data/data/com.termux/files/home,false) Continue Task After Error:On ] + + A68: Variable Set [ + Name:%command_failed + To:err = `%err` + + errmsg = + ``` + %errmsg + ``` + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] If [ %err Set | %errmsg Set ] + + A69: If [ %command_failed Set ] + + A70: Text Dialog [ + Title:Template 4 Command + Failed + Text:%command_failed + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A71: Stop [ + With Error:Off + Task: ] + + A72: End If + + <Template 4 End> + A73: Anchor + + <Return> + A74: Anchor +`````` + +## +  + + +*This file was automatically generated using [tasker_config_utils v0.5.0](https://github.com/Taskomater/tasker_config_utils).* diff --git a/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.sha256sums b/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.sha256sums new file mode 100755 index 0000000..e1163e3 --- /dev/null +++ b/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.sha256sums @@ -0,0 +1,2 @@ +56ff886e8d5e2bc0f3dc9add9e3942d28351d79d144b17c88a9c8b6d04cf92fc Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.xml +80e1187895e3a2ddeb2d2db5d4e1cfcf7a7ef467ed4816180f389d60c27709cd Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.md diff --git a/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.xml b/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.xml new file mode 100755 index 0000000..b43ae3d --- /dev/null +++ b/templates/plugin_hosts/tasker/Termux_RUN_COMMAND_Intent_Sudo_Templates.tsk.xml @@ -0,0 +1,950 @@ +<TaskerData sr="" dvi="1" tv="5.11.7.beta"> + <Task sr="task999"> + <cdate>1604448827988</cdate> + <edate>1607961014062</edate> + <id>999</id> + <nme>Termux RUN_COMMAND Intent Sudo Templates</nme> + <pri>100</pri> + <Share sr="Share"> + <d>A task that provides templates for running `sudo` script commands with the `RUN_COMMAND` intent in `superuser (root)` context in termux. The commands are run using the Tasker `TermuxCommand()` function of the `Tasker Function` action and with the `am` command with the `Run Shell` action and are referred as intent actions in this task. This task requires Termux:Tasker version `>= 0.5`. Tasker must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file, otherwise any commands received via the `RUN_COMMAND` intent will not be executed by Termux. For android `>= 10`, Termux must also be granted `Draw Over Apps` permissions so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like `SuperSU` or `Magisk` for the `sudo` script to work. + +Check [Termux:Tasker Github](https://github.com/termux/termux-tasker) and [RunCommand Intent](https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/RunCommandService.java) for more details on `RUN_COMMAND` intent configuration. + +Check [sudo](https://github.com/agnostic-apollo/sudo) for more details for the `sudo` script. + + +The result of commands is not received back when commands are run with the `RUN_COMMAND` intent, hence all templates run their commands in the foreground terminal session so that the results can be viewed. The `--sleep=3` command option is also passed to `sudo` so that it sleeps for `3` seconds before exiting. If this is not passed, the terminal would immediately close after `sudo` executes its commands without giving the user a chance to view the results in the terminal session. Moreover, a `Wait` action of `2` seconds is also added to the task after each template so that the templates execute in order and don't start until the previous one has at least ideally started executing. + +The default value of the `%comma_alternative` variable in this task is set to `‚` (`#U+201A`, `&sbquo;`, `&#8218;`, `single low-9 quotation mark`). This is the same character that is replaced with the simple comma `,` (`U+002C`, `&comma;`, `&#44;`, `comma`) by the `sudo` script by default when the `-r` option is passed. You can use a different character that should be replaced using the `--comma-alternative` option. + + +Template 1 runs the `$PREFIX/bin/sudo --sleep=3 dumpsys -l` command in the foreground using `TermuxCommand()` function as a template for the `path` `command_type` to list all android services. The arguments do not contain any simple comma characters and hence can be passed directly without having to replace them with `%comma_alternative` characters. + + +Template 2 runs the `$PREFIX/bin/sudo -sr --sleep=3 '%core_script' '%argument_1' '%argument_2'` command in the foreground using `TermuxCommand()` function as a template for the `script` `command_type` with `bash` as the `sudo shell`, which would be chosen automatically by default without having to pass the `--shell` option. The `termux_tasker_basic_bash_test` `bash` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain simple comma characters. The `-s` command option is passed to set `command_type` to `script`. The `-r` command option is passed so that `sudo` parses the arguments as per `RUN_COMMAND` intent rules. The `sudo` executable is passed to the intent using the `%executable` variable. The `%core_script`, `%argument_1` and `%argument_2` variables are used to store the dynamic values to be sent as `$1`, `$2` and `$3` respectively to `sudo`. They are first all set in the `%arguments` variable separated by simple comma characters, which is passed to the intent. The `%core_script`, `%argument_1` and `%argument_2` variables may contain any type of characters, even a simple comma, but simple commas must be replaced with the `%comma_alternative` characters before the intent action is run so that their values are not split into multiple arguments by the intent. To replace the simple commas, `Variable Search Replace` action is run to replace all simple commas `,` with the `%comma_alternative` variable value. The `Variable Search Replace` action must be used separately for each argument variable before adding it to the `%arguments` variable. Do not set multiple arguments in the same variable and use `Variable Search Replace` action on it since that will result in incorrect argument splitting. This template shows how you can dynamically create intent commands at runtime using variables and send them via the `RUN_COMMAND` intent to Termux for execution, including passing the script text itself without having to create a physical file in `~/.termux/tasker/` directory. + + +Template 3 is almost the same as Template 2, but it runs the `$PREFIX/bin/sudo -sr --shell=python --sleep=3 '%core_script' '%argument_1' '%argument_2'` command in the foreground using `am` command as a template for the `script` `command_type` with `python` as the `sudo shell`. The `termux_tasker_basic_python_test` `python` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain simple comma characters. However, as already mentioned that this template uses the `am` command, the `%arguments` variable is passed to the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra surrounded with single quotes inside a shell and hence any single quotes inside the `%arguments` variable also need to be escaped before running the intent command to prevent incorrect quoting. To escape the single quotes, `Variable Search Replace` action is run to replace all single quotes `'` with one single quote, followed by one backslash, followed by two single quotes `'\''`. So `%arguments` surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed as the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra value. The `Variable Search Replace` action does not need to be used separately for each argument variable before adding it to the `%arguments` variable, running it on only the final `%arguments` variable would be fine, unlike how its done on individual argument variables like `%argument_1`, etc for usage with `Termux:Tasker` plugin. + + +Template 4 runs `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` in the foreground using `TermuxCommand()` function as a template for the `su` `command_type` to start an interactive `bash` shell with priority to termux binaries and libraries. The `--shell-pre-commands` option is passed to run some commands before starting the interactive `bash` shell and its argument is **not** passed surrounded with single or double quotes to prevent whitespace splitting in the intent action, like done for usage with `Termux:Tasker` plugin since splitting will occur on simple comma characters instead. If there are going to be simple comma characters in arguments to the command options or in the main arguments to `sudo`, you must replace them with `%comma_alternative` variable value and pass the `-r` option. The `--title` option is passed to set the title of the terminal. + + +The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. These shortcuts can be used in any path arguments to the `sudo` command. + + +The `%command_failed` variable will be set if the intent action failed, this is detected by whether `%err` or `%errmsg` is set by the intent action. If you run multiple intent actions in the same task or are using `Local Variable Passthrough`, then you must clear the `%command_failed` variable and optionally the `%errmsg` variable with the `Variable Clear` action before running each intent action, in case they were already set, like by a previously failed intent action after which the task was not stopped. +## + + +**Parameters:** `-` + + +**Returns:** `-` + + +**Control:** + +``` +version_name: 0.1.0 +```</d> + <p>false</p> + <t></t> + </Share> + <Action sr="act0" ve="7"> + <code>300</code> + <label>A task that provides templates for running `sudo` script commands with the `RUN_COMMAND` intent in `superuser (root)` context in termux. The commands are run using the Tasker `TermuxCommand()` function of the `Tasker Function` action and with the `am` command with the `Run Shell` action and are referred as intent actions in this task. This task requires Termux:Tasker version `>= 0.5`. Tasker must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file, otherwise any commands received via the `RUN_COMMAND` intent will not be executed by Termux. For android `>= 10`, Termux must also be granted `Draw Over Apps` permissions so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like `SuperSU` or `Magisk` for the `sudo` script to work. + +Check [Termux:Tasker Github](https://github.com/termux/termux-tasker) and [RunCommand Intent](https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/RunCommandService.java) for more details on `RUN_COMMAND` intent configuration. + +Check [sudo](https://github.com/agnostic-apollo/sudo) for more details for the `sudo` script. + + +The result of commands is not received back when commands are run with the `RUN_COMMAND` intent, hence all templates run their commands in the foreground terminal session so that the results can be viewed. The `--sleep=3` command option is also passed to `sudo` so that it sleeps for `3` seconds before exiting. If this is not passed, the terminal would immediately close after `sudo` executes its commands without giving the user a chance to view the results in the terminal session. Moreover, a `Wait` action of `2` seconds is also added to the task after each template so that the templates execute in order and don't start until the previous one has at least ideally started executing. + +The default value of the `%comma_alternative` variable in this task is set to `‚` (`#U+201A`, `&sbquo;`, `&#8218;`, `single low-9 quotation mark`). This is the same character that is replaced with the simple comma `,` (`U+002C`, `&comma;`, `&#44;`, `comma`) by the `sudo` script by default when the `-r` option is passed. You can use a different character that should be replaced using the `--comma-alternative` option. + + +Template 1 runs the `$PREFIX/bin/sudo --sleep=3 dumpsys -l` command in the foreground using `TermuxCommand()` function as a template for the `path` `command_type` to list all android services. The arguments do not contain any simple comma characters and hence can be passed directly without having to replace them with `%comma_alternative` characters. + + +Template 2 runs the `$PREFIX/bin/sudo -sr --sleep=3 '%core_script' '%argument_1' '%argument_2'` command in the foreground using `TermuxCommand()` function as a template for the `script` `command_type` with `bash` as the `sudo shell`, which would be chosen automatically by default without having to pass the `--shell` option. The `termux_tasker_basic_bash_test` `bash` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain simple comma characters. The `-s` command option is passed to set `command_type` to `script`. The `-r` command option is passed so that `sudo` parses the arguments as per `RUN_COMMAND` intent rules. The `sudo` executable is passed to the intent using the `%executable` variable. The `%core_script`, `%argument_1` and `%argument_2` variables are used to store the dynamic values to be sent as `$1`, `$2` and `$3` respectively to `sudo`. They are first all set in the `%arguments` variable separated by simple comma characters, which is passed to the intent. The `%core_script`, `%argument_1` and `%argument_2` variables may contain any type of characters, even a simple comma, but simple commas must be replaced with the `%comma_alternative` characters before the intent action is run so that their values are not split into multiple arguments by the intent. To replace the simple commas, `Variable Search Replace` action is run to replace all simple commas `,` with the `%comma_alternative` variable value. The `Variable Search Replace` action must be used separately for each argument variable before adding it to the `%arguments` variable. Do not set multiple arguments in the same variable and use `Variable Search Replace` action on it since that will result in incorrect argument splitting. This template shows how you can dynamically create intent commands at runtime using variables and send them via the `RUN_COMMAND` intent to Termux for execution, including passing the script text itself without having to create a physical file in `~/.termux/tasker/` directory. + + +Template 3 is almost the same as Template 2, but it runs the `$PREFIX/bin/sudo -sr --shell=python --sleep=3 '%core_script' '%argument_1' '%argument_2'` command in the foreground using `am` command as a template for the `script` `command_type` with `python` as the `sudo shell`. The `termux_tasker_basic_python_test` `python` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain simple comma characters. However, as already mentioned that this template uses the `am` command, the `%arguments` variable is passed to the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra surrounded with single quotes inside a shell and hence any single quotes inside the `%arguments` variable also need to be escaped before running the intent command to prevent incorrect quoting. To escape the single quotes, `Variable Search Replace` action is run to replace all single quotes `'` with one single quote, followed by one backslash, followed by two single quotes `'\''`. So `%arguments` surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed as the `com.termux.RUN_COMMAND_ARGUMENTS` string array extra value. The `Variable Search Replace` action does not need to be used separately for each argument variable before adding it to the `%arguments` variable, running it on only the final `%arguments` variable would be fine, unlike how its done on individual argument variables like `%argument_1`, etc for usage with `Termux:Tasker` plugin. + + +Template 4 runs `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` in the foreground using `TermuxCommand()` function as a template for the `su` `command_type` to start an interactive `bash` shell with priority to termux binaries and libraries. The `--shell-pre-commands` option is passed to run some commands before starting the interactive `bash` shell and its argument is **not** passed surrounded with single or double quotes to prevent whitespace splitting in the intent action, like done for usage with `Termux:Tasker` plugin since splitting will occur on simple comma characters instead. If there are going to be simple comma characters in arguments to the command options or in the main arguments to `sudo`, you must replace them with `%comma_alternative` variable value and pass the `-r` option. The `--title` option is passed to set the title of the terminal. + + +The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. These shortcuts can be used in any path arguments to the `sudo` command. + + +The `%command_failed` variable will be set if the intent action failed, this is detected by whether `%err` or `%errmsg` is set by the intent action. If you run multiple intent actions in the same task or are using `Local Variable Passthrough`, then you must clear the `%command_failed` variable and optionally the `%errmsg` variable with the `Variable Clear` action before running each intent action, in case they were already set, like by a previously failed intent action after which the task was not stopped. +## + + +**Parameters:** `-` + + +**Returns:** `-` + + +**Control:** + +``` +version_name: 0.1.0 +```</label> + </Action> + <Action sr="act1" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%task_name</Str> + <Str sr="arg1" ve="3">Termux RUN_COMMAND Intent Sudo Templates</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act10" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 1 Command +Failed</Str> + <Str sr="arg2" ve="3">%command_failed</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act11" ve="7"> + <code>137</code> + <Int sr="arg0" val="0"/> + <Str sr="arg1" ve="3"/> + </Action> + <Action sr="act12" ve="7"> + <code>38</code> + </Action> + <Action sr="act13" ve="7"> + <code>300</code> + <label>Template 1 End</label> + </Action> + <Action sr="act14" ve="7"> + <code>30</code> + <Int sr="arg0" val="0"/> + <Int sr="arg1" val="2"/> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + </Action> + <Action sr="act15" ve="7"> + <code>300</code> + <label>Template 2 Start</label> + </Action> + <Action sr="act16" ve="7"> + <code>135</code> + <label>Goto "Template 3 Start" +Enable this action to skip running this template</label> + <on>false</on> + <Int sr="arg0" val="1"/> + <Int sr="arg1" val="1"/> + <Str sr="arg2" ve="3">Template 3 Start</Str> + </Action> + <Action sr="act17" ve="7"> + <code>300</code> + <label>Run `$PREFIX/bin/sudo -sr --sleep=3 '%core_script' '%argument_1' '%argument_2'` Command In Foreground</label> + </Action> + <Action sr="act18" ve="7"> + <code>547</code> + <label>set `‚` (`#U+201A`, `&sbquo;`, `&#8218;`, `single low-9 quotation mark`) to %comma_alternative</label> + <Str sr="arg0" ve="3">%comma_alternative</Str> + <Str sr="arg1" ve="3">‚</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act19" ve="7"> + <code>547</code> + <label>set `$PREFIX/bin/sudo` to %executable</label> + <Str sr="arg0" ve="3">%executable</Str> + <Str sr="arg1" ve="3">$PREFIX/bin/sudo</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act2" ve="7"> + <code>300</code> + <label>Template 1 Start</label> + </Action> + <Action sr="act20" ve="7"> + <code>547</code> + <label>set `termux_tasker_basic_bash_test` script text to %core_script</label> + <Str sr="arg0" ve="3">%core_script</Str> + <Str sr="arg1" ve="3">#if parameter count is not 2 +if [ $# -ne 2 ]; then + echo "Invalid parameter count '$#' to 'termux_tasker_basic_bash_test'" 1>&2 + echo "$*" 1>&2 + exit 1 +fi + +echo "\$1=\`$1\`" +echo "\$2=\`$2\`" + +exit 0</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act21" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%argument_1</Str> + <Str sr="arg1" ve="3">json</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act22" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%argument_2</Str> + <Str sr="arg1" ve="3">{ + "name":"I'm Termux", + "license":"GPLv3", + "addons": { + "1":"Termux:API", + "2":"Termux:Boot", + "3":"Termux:Float", + "4":"Termux:Styling", + "5":"Termux:Tasker", + "6":"Termux:Widget" + } +}</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act23" ve="7"> + <code>598</code> + <label>replace all simple commas `,` (`U+002C`, `&comma;`, `&#44;`, `comma`) with %comma_alternative characters</label> + <Str sr="arg0" ve="3">%core_script</Str> + <Str sr="arg1" ve="3">,</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">%comma_alternative</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%core_script</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act24" ve="7"> + <code>598</code> + <label>replace all simple commas `,` (`U+002C`, `&comma;`, `&#44;`, `comma`) with %comma_alternative characters</label> + <Str sr="arg0" ve="3">%argument_1</Str> + <Str sr="arg1" ve="3">,</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">%comma_alternative</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%argument_1</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act25" ve="7"> + <code>598</code> + <label>replace all simple commas `,` (`U+002C`, `&comma;`, `&#44;`, `comma`) with %comma_alternative characters</label> + <Str sr="arg0" ve="3">%argument_2</Str> + <Str sr="arg1" ve="3">,</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">%comma_alternative</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%argument_2</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act26" ve="7"> + <code>547</code> + <label>set `-sr,--sleep=3,%core_script,%argument_1,%argument_2` to %arguments</label> + <Str sr="arg0" ve="3">%arguments</Str> + <Str sr="arg1" ve="3">-sr,--sleep=3,%core_script,%argument_1,%argument_2</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act27" ve="7"> + <code>547</code> + <label>set `%executable,%arguments,/data/data/com.termux/files/home,false` to %termux_command</label> + <Str sr="arg0" ve="3">%termux_command</Str> + <Str sr="arg1" ve="3">%executable,%arguments,/data/data/com.termux/files/home,false</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act28" ve="7"> + <code>549</code> + <Str sr="arg0" ve="3">%command_failed/%errmsg</Str> + <Int sr="arg1" val="1"/> + <Int sr="arg2" val="1"/> + <Int sr="arg3" val="0"/> + </Action> + <Action sr="act29" ve="7"> + <code>365</code> + <label>Run Termux RUN_COMMAND Intent Command with TermuxCommand() Function</label> + <se>false</se> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""/></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">TermuxCommand(%termux_command)</Str> + </Action> + <Action sr="act3" ve="7"> + <code>135</code> + <label>Goto "Template 2 Start" +Enable this action to skip running this template</label> + <on>false</on> + <Int sr="arg0" val="1"/> + <Int sr="arg1" val="1"/> + <Str sr="arg2" ve="3">Template 2 Start</Str> + </Action> + <Action sr="act30" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">err = `%err` + +errmsg = +``` +%errmsg +```</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + <ConditionList sr="if"> + <bool0>Or</bool0> + <Condition sr="c0" ve="3"> + <lhs>%err</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c1" ve="3"> + <lhs>%errmsg</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act31" ve="7"> + <code>37</code> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs>\%err +\%errmsg</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act32" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 2 Command +Failed</Str> + <Str sr="arg2" ve="3">%command_failed</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act33" ve="7"> + <code>137</code> + <Int sr="arg0" val="0"/> + <Str sr="arg1" ve="3"/> + </Action> + <Action sr="act34" ve="7"> + <code>38</code> + </Action> + <Action sr="act35" ve="7"> + <code>300</code> + <label>Template 2 End</label> + </Action> + <Action sr="act36" ve="7"> + <code>30</code> + <Int sr="arg0" val="0"/> + <Int sr="arg1" val="2"/> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + </Action> + <Action sr="act37" ve="7"> + <code>300</code> + <label>Template 3 Start</label> + </Action> + <Action sr="act38" ve="7"> + <code>135</code> + <label>Goto "Template 4 Start" +Enable this action to skip running this template</label> + <on>false</on> + <Int sr="arg0" val="1"/> + <Int sr="arg1" val="1"/> + <Str sr="arg2" ve="3">Template 4 Start</Str> + </Action> + <Action sr="act39" ve="7"> + <code>300</code> + <label>Run `$PREFIX/bin/sudo -sr --shell=python --sleep=3 '%core_script' '%argument_1' '%argument_2'` Command In Foreground</label> + </Action> + <Action sr="act4" ve="7"> + <code>300</code> + <label>Run `$PREFIX/bin/sudo --sleep=3 dumpsys -l` Command In Foreground</label> + </Action> + <Action sr="act40" ve="7"> + <code>547</code> + <label>set `‚` (`#U+201A`, `&sbquo;`, `&#8218;`, `single low-9 quotation mark`) to %comma_alternative</label> + <Str sr="arg0" ve="3">%comma_alternative</Str> + <Str sr="arg1" ve="3">‚</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act41" ve="7"> + <code>547</code> + <label>set `$PREFIX/bin/sudo` to %executable</label> + <Str sr="arg0" ve="3">%executable</Str> + <Str sr="arg1" ve="3">$PREFIX/bin/sudo</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act42" ve="7"> + <code>547</code> + <label>set `termux_tasker_basic_python_test` script text to %core_script</label> + <Str sr="arg0" ve="3">%core_script</Str> + <Str sr="arg1" ve="3">import sys + +argv_size = len(sys.argv) - 1 + +# if parameter count is not 2 +if argv_size != 2: + print("Invalid parameter count '%s' to 'termux_tasker_basic_python_test'" % argv_size, file=sys.stderr) + print("%s" % " ".join(sys.argv[1:]), file=sys.stderr) + sys.exit(1) + +print("$1=`%s`" % sys.argv[1]) +print("$2=`%s`" % sys.argv[2]) + +sys.exit(0)</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act43" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%argument_1</Str> + <Str sr="arg1" ve="3">json</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act44" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%argument_2</Str> + <Str sr="arg1" ve="3">{ + "name":"I'm Termux", + "license":"GPLv3", + "addons": { + "1":"Termux:API", + "2":"Termux:Boot", + "3":"Termux:Float", + "4":"Termux:Styling", + "5":"Termux:Tasker", + "6":"Termux:Widget" + } +}</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act45" ve="7"> + <code>598</code> + <label>replace all simple commas `,` (`U+002C`, `&comma;`, `&#44;`, `comma`) with %comma_alternative characters</label> + <Str sr="arg0" ve="3">%core_script</Str> + <Str sr="arg1" ve="3">,</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">%comma_alternative</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%core_script</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act46" ve="7"> + <code>598</code> + <label>replace all simple commas `,` (`U+002C`, `&comma;`, `&#44;`, `comma`) with %comma_alternative characters</label> + <Str sr="arg0" ve="3">%argument_1</Str> + <Str sr="arg1" ve="3">,</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">%comma_alternative</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%argument_1</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act47" ve="7"> + <code>598</code> + <label>replace all simple commas `,` (`U+002C`, `&comma;`, `&#44;`, `comma`) with %comma_alternative characters</label> + <Str sr="arg0" ve="3">%argument_2</Str> + <Str sr="arg1" ve="3">,</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">%comma_alternative</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%argument_2</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act48" ve="7"> + <code>547</code> + <label>set `-sr,--shell=python,--sleep=3,%core_script,%argument_1,%argument_2` to %arguments</label> + <Str sr="arg0" ve="3">%arguments</Str> + <Str sr="arg1" ve="3">-sr,--shell=python,--sleep=3,%core_script,%argument_1,%argument_2</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act49" ve="7"> + <code>598</code> + <label>replace all single quotes (') with ('\'')</label> + <Str sr="arg0" ve="3">%arguments</Str> + <Str sr="arg1" ve="3">'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="1"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">'\\''</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%arguments</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act5" ve="7"> + <code>549</code> + <Str sr="arg0" ve="3">%command_failed/%errmsg</Str> + <Int sr="arg1" val="1"/> + <Int sr="arg2" val="1"/> + <Int sr="arg3" val="0"/> + </Action> + <Action sr="act50" ve="7"> + <code>549</code> + <Str sr="arg0" ve="3">%command_failed/%errmsg</Str> + <Int sr="arg1" val="1"/> + <Int sr="arg2" val="1"/> + <Int sr="arg3" val="0"/> + </Action> + <Action sr="act51" ve="7"> + <code>123</code> + <label>Run Termux RUN_COMMAND Intent Command with `am` Command</label> + <se>false</se> + <Str sr="arg0" ve="3">am startservice --user 0 -n com.termux/com.termux.app.RunCommandService -a com.termux.RUN_COMMAND --es com.termux.RUN_COMMAND_PATH '%executable' --esa com.termux.RUN_COMMAND_ARGUMENTS '%arguments' --es com.termux.RUN_COMMAND_WORKDIR '/data/data/com.termux/files/home' --ez com.termux.RUN_COMMAND_BACKGROUND 'false'</Str> + <Int sr="arg1" val="0"/> + <Int sr="arg2" val="0"/> + <Str sr="arg3" ve="3">%stdout</Str> + <Str sr="arg4" ve="3">%stderr</Str> + <Str sr="arg5" ve="3"/> + </Action> + <Action sr="act52" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">err = `%err` + +errmsg = +``` +%errmsg +``` + +stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + <ConditionList sr="if"> + <bool0>Or</bool0> + <Condition sr="c0" ve="3"> + <lhs>%err</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c1" ve="3"> + <lhs>%errmsg</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act53" ve="7"> + <code>37</code> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs>\%err +\%errmsg</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act54" ve="7"> + <code>598</code> + <label>remove %err and %errmsg if not set</label> + <se>false</se> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3"/> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act55" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 3 Command +Failed</Str> + <Str sr="arg2" ve="3">%command_failed</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act56" ve="7"> + <code>137</code> + <Int sr="arg0" val="0"/> + <Str sr="arg1" ve="3"/> + </Action> + <Action sr="act57" ve="7"> + <code>43</code> + </Action> + <Action sr="act58" ve="7"> + <code>377</code> + <on>false</on> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 3 Command Result</Str> + <Str sr="arg2" ve="3">stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act59" ve="7"> + <code>38</code> + </Action> + <Action sr="act6" ve="7"> + <code>365</code> + <label>Run Termux RUN_COMMAND Intent Command with TermuxCommand() Function</label> + <se>false</se> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""/></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">TermuxCommand($PREFIX/bin/sudo,--sleep=3,dumpsys,-l,/data/data/com.termux/files/home,false)</Str> + </Action> + <Action sr="act60" ve="7"> + <code>300</code> + <label>Template 3 End</label> + </Action> + <Action sr="act61" ve="7"> + <code>30</code> + <Int sr="arg0" val="0"/> + <Int sr="arg1" val="2"/> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + </Action> + <Action sr="act62" ve="7"> + <code>300</code> + <label>Template 4 Start</label> + </Action> + <Action sr="act63" ve="7"> + <code>135</code> + <label>Goto "Return" +Enable this action to skip running this template</label> + <on>false</on> + <Int sr="arg0" val="1"/> + <Int sr="arg1" val="1"/> + <Str sr="arg2" ve="3">Return</Str> + </Action> + <Action sr="act64" ve="7"> + <code>300</code> + <label>Run `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` Command In Foreground</label> + </Action> + <Action sr="act65" ve="7"> + <code>549</code> + <Str sr="arg0" ve="3">%command_failed/%errmsg</Str> + <Int sr="arg1" val="1"/> + <Int sr="arg2" val="1"/> + <Int sr="arg3" val="0"/> + </Action> + <Action sr="act66" ve="7"> + <code>365</code> + <label>Run Termux RUN_COMMAND Intent Command with TermuxCommand() Function</label> + <se>false</se> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""/></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">TermuxCommand($PREFIX/bin/sudo,--shell-pre-commands=echo 'starting sudo shell';,--title='sudo',su,/data/data/com.termux/files/home,false)</Str> + </Action> + <Action sr="act67" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">err = `%err` + +errmsg = +``` +%errmsg +```</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + <ConditionList sr="if"> + <bool0>Or</bool0> + <Condition sr="c0" ve="3"> + <lhs>%err</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c1" ve="3"> + <lhs>%errmsg</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act68" ve="7"> + <code>37</code> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs>\%err +\%errmsg</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act69" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 4 Command +Failed</Str> + <Str sr="arg2" ve="3">%command_failed</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act7" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">err = `%err` + +errmsg = +``` +%errmsg +``` + +stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + <ConditionList sr="if"> + <bool0>Or</bool0> + <Condition sr="c0" ve="3"> + <lhs>%err</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c1" ve="3"> + <lhs>%errmsg</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act70" ve="7"> + <code>137</code> + <Int sr="arg0" val="0"/> + <Str sr="arg1" ve="3"/> + </Action> + <Action sr="act71" ve="7"> + <code>38</code> + </Action> + <Action sr="act72" ve="7"> + <code>300</code> + <label>Template 4 End</label> + </Action> + <Action sr="act73" ve="7"> + <code>300</code> + <label>Return</label> + </Action> + <Action sr="act8" ve="7"> + <code>37</code> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs>\%err +\%errmsg</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act9" ve="7"> + <code>598</code> + <label>remove %err and %errmsg if not set</label> + <se>false</se> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3"/> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + </Task> +</TaskerData> diff --git a/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.md b/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.md new file mode 100755 index 0000000..df97bdf --- /dev/null +++ b/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.md @@ -0,0 +1,709 @@ +# Termux Tasker Plugin Sudo Templates + +  +## Export Info: +**Tasker Version:** `5.11.7.beta` +**Timestamp:** `2020-12-14 15.53.34` +  + + + +  +## Profile Names: +**Count:** `0` + + + + +## Scene Names: +**Count:** `0` + + + + +## Task Names: +**Count:** `1` + +- `Termux Tasker Plugin Sudo Templates` +## +  + + + +  +## Profiles Info: +  + + + +  +## Tasks Info: +  + +### Task 1 +**Name:** `Termux Tasker Plugin Sudo Templates` +**ID:** `992` +**Collision Handling:** `Abort New Task` +**Keep Device Awake:** `false` + +#### Help: + +A task that provides templates for running `sudo` script commands with the Termux:Tasker plugin in `superuser (root)` context in termux. This task requires Termux:Tasker version `>= 0.5`. Tasker must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file since the `$PREFIX/bin/sudo` absolute path is outside the `~/.termux/tasker/` directory, otherwise the plugin actions will fail. For android `>= 10`, Termux must also be granted `Draw Over Apps` permissions so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like `SuperSU` or `Magisk` for the `sudo` script to work. + +Check [Termux:Tasker Github](https://github.com/termux/termux-tasker) for more details on plugin configuration and variables and how to handle them. + +Check [sudo](https://github.com/agnostic-apollo/sudo) for more details for the `sudo` script. + + +Template 1 runs the `$PREFIX/bin/sudo dumpsys -l` command in the background as a template for the `path` `command_type` to list all android services. The args sent do not contain any quotes or special characters and can simply be sent, optionally surrounded with double quotes. + + +Template 2 runs the `$PREFIX/bin/sudo -s '%core_script' '%argument_1' '%argument_2'` command in the background as a template for the `script` `command_type` with `bash` as the `sudo shell`, which would be chosen automatically by default without having to pass the `--shell` option. The `termux_tasker_basic_bash_test` `bash` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain quotes or special characters, basically a literal string. The `-s` command option is passed to set `command_type` to `script`. The result is received back in the `%stdout` variable. The `sudo` executable is passed to the plugin using the `%executable` variable. The `%core_script`, `%argument_1` and `%argument_2` variables are used to store the dynamic values to be sent as `$1`, `$2` and `$3` respectively to `sudo` and are sent surrounded with single quotes instead of double quotes. They are first all set in the `%arguments` variable surrounded with single quotes and separated by a whitespace which is passed to the plugin. The `%core_script`, `%argument_1` and `%argument_2` variables may contain any type of characters, even a single quote, but single quotes must be escaped before the plugin action is run. To escape the single quotes, `Variable Search Replace` action is run to replace all single quotes `'` with one single quote, followed by one backslash, followed by two single quotes `'\''`. So `%argument_1` surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed to the executable as `$2`. The `Variable Search Replace` action must be used separately for each argument variable before adding it to the `%arguments` variable. Do not set multiple arguments in the same variable and use `Variable Search Replace` action on it since that will result in incorrect quoting. This template shows how you can dynamically create plugin commands at runtime using variables and send them to the plugin for execution, including passing the script text itself without having to create a physical file in `~/.termux/tasker/` directory. + + +Template 3 is almost the same as Template 2, but it runs the `$PREFIX/bin/sudo -s --shell=python '%core_script' '%argument_1' '%argument_2'` command in the background as a template for the `script` `command_type` with `python` as the `sudo shell`. The `termux_tasker_basic_python_test` `python` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain quotes or special characters, basically a literal string. The result is received back in the `%stdout` variable. + + +Template 4 runs `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` in the foreground as a template for the `su` `command_type` to start an interactive `bash` shell with priority to termux binaries and libraries. The `--shell-pre-commands` option is passed to run some commands before starting the interactive `bash` shell and its argument is passed surrounded with double quotes to prevent whitespace splitting. The `--title` option is passed to set the title of the terminal. Since commands will be run in a foreground terminal session, the `%stdout`, `%stderr` and `%result` variables will not be returned and only `%err` and `%errmsg` may be returned if the action fails. + + +The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. These shortcuts can be used in the `Executable` and the `Working Directory` plugin fields and in any path arguments to the `sudo` command. The scripts or binaries in the `~/.termux/tasker/` directory do not require them to prefixed with the full path, just set the name in the `Executable` field. + + +The `%command_failed` variable will be set if the plugin action failed, this is detected by whether `%err` or `%errmsg` is set by the plugin action or if `%result` does not equal `0` for background commands. If you run multiple plugin actions in the same task or are using `Local Variable Passthrough`, then you must clear the `%command_failed` variable and optionally the `%errmsg`, `%stdout`, `%stderr` and `%result` variables with the `Variable Clear` action before running each plugin action, in case they were already set, like by a previously failed plugin action after which the task was not stopped. + + +To debug arguments being passed or any errors, you can check `logcat` after increasing log level to `Debug`. Check `Debugging` section of `README.md` for more details. +## + + +**Parameters:** `-` + + +**Returns:** `-` + + +**Control:** + +``` +version_name: 0.1.0 +``` +## +  + + + +  +## Code Description: +  + +`````` +Task Name: Termux Tasker Plugin Sudo Templates + +Actions: + <A task that provides templates for running `sudo` script commands with the Termux:Tasker plugin in `superuser (root)` context in termux. This task requires Termux:Tasker version `>= 0.5`. Tasker must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file since the `$PREFIX/bin/sudo` absolute path is outside the `~/.termux/tasker/` directory, otherwise the plugin actions will fail. For android `>= 10`, Termux must also be granted `Draw Over Apps` permissions so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like `SuperSU` or `Magisk` for the `sudo` script to work. + + Check [Termux:Tasker Github](https://github.com/termux/termux-tasker) for more details on plugin configuration and variables and how to handle them. + + Check [sudo](https://github.com/agnostic-apollo/sudo) for more details for the `sudo` script. + + + Template 1 runs the `$PREFIX/bin/sudo dumpsys -l` command in the background as a template for the `path` `command_type` to list all android services. The args sent do not contain any quotes or special characters and can simply be sent, optionally surrounded with double quotes. + + + Template 2 runs the `$PREFIX/bin/sudo -s '%core_script' '%argument_1' '%argument_2'` command in the background as a template for the `script` `command_type` with `bash` as the `sudo shell`, which would be chosen automatically by default without having to pass the `--shell` option. The `termux_tasker_basic_bash_test` `bash` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain quotes or special characters, basically a literal string. The `-s` command option is passed to set `command_type` to `script`. The result is received back in the `%stdout` variable. The `sudo` executable is passed to the plugin using the `%executable` variable. The `%core_script`, `%argument_1` and `%argument_2` variables are used to store the dynamic values to be sent as `$1`, `$2` and `$3` respectively to `sudo` and are sent surrounded with single quotes instead of double quotes. They are first all set in the `%arguments` variable surrounded with single quotes and separated by a whitespace which is passed to the plugin. The `%core_script`, `%argument_1` and `%argument_2` variables may contain any type of characters, even a single quote, but single quotes must be escaped before the plugin action is run. To escape the single quotes, `Variable Search Replace` action is run to replace all single quotes `'` with one single quote, followed by one backslash, followed by two single quotes `'\''`. So `%argument_1` surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed to the executable as `$2`. The `Variable Search Replace` action must be used separately for each argument variable before adding it to the `%arguments` variable. Do not set multiple arguments in the same variable and use `Variable Search Replace` action on it since that will result in incorrect quoting. This template shows how you can dynamically create plugin commands at runtime using variables and send them to the plugin for execution, including passing the script text itself without having to create a physical file in `~/.termux/tasker/` directory. + + + Template 3 is almost the same as Template 2, but it runs the `$PREFIX/bin/sudo -s --shell=python '%core_script' '%argument_1' '%argument_2'` command in the background as a template for the `script` `command_type` with `python` as the `sudo shell`. The `termux_tasker_basic_python_test` `python` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain quotes or special characters, basically a literal string. The result is received back in the `%stdout` variable. + + + Template 4 runs `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` in the foreground as a template for the `su` `command_type` to start an interactive `bash` shell with priority to termux binaries and libraries. The `--shell-pre-commands` option is passed to run some commands before starting the interactive `bash` shell and its argument is passed surrounded with double quotes to prevent whitespace splitting. The `--title` option is passed to set the title of the terminal. Since commands will be run in a foreground terminal session, the `%stdout`, `%stderr` and `%result` variables will not be returned and only `%err` and `%errmsg` may be returned if the action fails. + + + The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. These shortcuts can be used in the `Executable` and the `Working Directory` plugin fields and in any path arguments to the `sudo` command. The scripts or binaries in the `~/.termux/tasker/` directory do not require them to prefixed with the full path, just set the name in the `Executable` field. + + + The `%command_failed` variable will be set if the plugin action failed, this is detected by whether `%err` or `%errmsg` is set by the plugin action or if `%result` does not equal `0` for background commands. If you run multiple plugin actions in the same task or are using `Local Variable Passthrough`, then you must clear the `%command_failed` variable and optionally the `%errmsg`, `%stdout`, `%stderr` and `%result` variables with the `Variable Clear` action before running each plugin action, in case they were already set, like by a previously failed plugin action after which the task was not stopped. + + + To debug arguments being passed or any errors, you can check `logcat` after increasing log level to `Debug`. Check `Debugging` section of `README.md` for more details. + ## + + + **Parameters:** `-` + + + **Returns:** `-` + + + **Control:** + + ``` + version_name: 0.1.0 + ```> + A1: Anchor + + A2: Variable Set [ + Name:%task_name + To:Termux Tasker Plugin Sudo Templates + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <Template 1 Start> + A3: Anchor + + <Goto "Template 2 Start" + Enable this action to skip running this template> + A4: [X] Goto [ + Type:Action Label + Number:1 + Label:Template 2 Start ] + + <Run `$PREFIX/bin/sudo dumpsys -l` Command In Background> + A5: Anchor + + A6: Variable Clear [ + Name:%command_failed/%errmsg/%stdout/%stderr/%result + Pattern Matching:On + Local Variables Only:On + Clear All Variables:Off ] + + <Run Termux:Tasker Plugin Command> + A7: Termux [ Configuration:$PREFIX/bin/sudo dumpsys -l Timeout (Seconds):10 Continue Task After Error:On ] + + A8: Variable Set [ + Name:%command_failed + To:err = `%err` + + errmsg = + ``` + %errmsg + ``` + + exit_code = `%result` + + stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] If [ %err Set | %errmsg Set | %result neq 0 ] + + A9: If [ %command_failed Set ] + + <remove %err and %errmsg if not set> + A10: Variable Search Replace [ + Variable:%command_failed + Search:^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+ + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With: Continue Task After Error:On ] If [ %command_failed Set ] + + A11: Text Dialog [ + Title:Template 1 Command + Failed + Text:%command_failed + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A12: Stop [ + With Error:Off + Task: ] + + A13: Else + + A14: Text Dialog [ + Title:Template 1 Command Result + Text:stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A15: End If + + <Template 1 End> + A16: Anchor + + <Template 2 Start> + A17: Anchor + + <Goto "Template 3 Start" + Enable this action to skip running this template> + A18: [X] Goto [ + Type:Action Label + Number:1 + Label:Template 3 Start ] + + <Run `$PREFIX/bin/sudo -s '%core_script' '%argument_1' '%argument_2'` Command In Background> + A19: Anchor + + <set `$PREFIX/bin/sudo` to %executable> + A20: Variable Set [ + Name:%executable + To:$PREFIX/bin/sudo + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <set `termux_tasker_basic_bash_test` script text to %core_script> + A21: Variable Set [ + Name:%core_script + To:#if parameter count is not 2 + if [ $# -ne 2 ]; then + echo "Invalid parameter count '$#' to 'termux_tasker_basic_bash_test'" 1>&2 + echo "$*" 1>&2 + exit 1 + fi + + echo "\$1=\`$1\`" + echo "\$2=\`$2\`" + + exit 0 + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A22: Variable Set [ + Name:%argument_1 + To:json + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A23: Variable Set [ + Name:%argument_2 + To:{ + "name":"I'm Termux", + "license":"GPLv3", + "addons": { + "1":"Termux:API", + "2":"Termux:Boot", + "3":"Termux:Float", + "4":"Termux:Styling", + "5":"Termux:Tasker", + "6":"Termux:Widget" + } + } + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <replace all single quotes (') with ('\'')> + A24: Variable Search Replace [ + Variable:%core_script + Search:' + Ignore Case:Off + Multi-Line:On + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:'\\'' ] If [ %core_script Set ] + + <replace all single quotes (') with ('\'')> + A25: Variable Search Replace [ + Variable:%argument_1 + Search:' + Ignore Case:Off + Multi-Line:On + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:'\\'' ] If [ %argument_1 Set ] + + <replace all single quotes (') with ('\'')> + A26: Variable Search Replace [ + Variable:%argument_2 + Search:' + Ignore Case:Off + Multi-Line:On + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:'\\'' ] If [ %argument_2 Set ] + + <set `-s '%core_script' '%argument_1' '%argument_2'` to %arguments> + A27: Variable Set [ + Name:%arguments + To:-s '%core_script' '%argument_1' '%argument_2' + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A28: Variable Clear [ + Name:%command_failed/%errmsg/%stdout/%stderr/%result + Pattern Matching:On + Local Variables Only:On + Clear All Variables:Off ] + + <Run Termux:Tasker Plugin Command> + A29: Termux [ Configuration:%executable %arguments Timeout (Seconds):10 Continue Task After Error:On ] + + A30: Variable Set [ + Name:%command_failed + To:err = `%err` + + errmsg = + ``` + %errmsg + ``` + + exit_code = `%result` + + stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] If [ %err Set | %errmsg Set | %result neq 0 ] + + A31: If [ %command_failed Set ] + + <remove %err and %errmsg if not set> + A32: Variable Search Replace [ + Variable:%command_failed + Search:^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+ + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With: Continue Task After Error:On ] If [ %command_failed Set ] + + A33: Text Dialog [ + Title:Template 2 Command + Failed + Text:%command_failed + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A34: Stop [ + With Error:Off + Task: ] + + A35: Else + + A36: Text Dialog [ + Title:Template 2 Command Result + Text:stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A37: End If + + <Template 2 End> + A38: Anchor + + <Template 3 Start> + A39: Anchor + + <Goto "Template 4 Start" + Enable this action to skip running this template> + A40: [X] Goto [ + Type:Action Label + Number:1 + Label:Template 4 Start ] + + <Run `$PREFIX/bin/sudo -s --shell=python '%core_script' '%argument_1' '%argument_2'` Command In Background> + A41: Anchor + + <set `$PREFIX/bin/sudo` to %executable> + A42: Variable Set [ + Name:%executable + To:$PREFIX/bin/sudo + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <set `termux_tasker_basic_python_test` script text to %core_script> + A43: Variable Set [ + Name:%core_script + To:import sys + + argv_size = len(sys.argv) - 1 + + # if parameter count is not 2 + if argv_size != 2: + print("Invalid parameter count '%s' to 'termux_tasker_basic_python_test'" % argv_size, file=sys.stderr) + print("%s" % " ".join(sys.argv[1:]), file=sys.stderr) + sys.exit(1) + + print("$1=`%s`" % sys.argv[1]) + print("$2=`%s`" % sys.argv[2]) + + sys.exit(0) + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A44: Variable Set [ + Name:%argument_1 + To:json + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A45: Variable Set [ + Name:%argument_2 + To:{ + "name":"I'm Termux", + "license":"GPLv3", + "addons": { + "1":"Termux:API", + "2":"Termux:Boot", + "3":"Termux:Float", + "4":"Termux:Styling", + "5":"Termux:Tasker", + "6":"Termux:Widget" + } + } + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + <replace all single quotes (') with ('\'')> + A46: Variable Search Replace [ + Variable:%core_script + Search:' + Ignore Case:Off + Multi-Line:On + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:'\\'' ] If [ %core_script Set ] + + <replace all single quotes (') with ('\'')> + A47: Variable Search Replace [ + Variable:%argument_1 + Search:' + Ignore Case:Off + Multi-Line:On + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:'\\'' ] If [ %argument_1 Set ] + + <replace all single quotes (') with ('\'')> + A48: Variable Search Replace [ + Variable:%argument_2 + Search:' + Ignore Case:Off + Multi-Line:On + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With:'\\'' ] If [ %argument_2 Set ] + + <set `-s --shell=python '%core_script' '%argument_1' '%argument_2'` to %arguments> + A49: Variable Set [ + Name:%arguments + To:-s --shell=python '%core_script' '%argument_1' '%argument_2' + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] + + A50: Variable Clear [ + Name:%command_failed/%errmsg/%stdout/%stderr/%result + Pattern Matching:On + Local Variables Only:On + Clear All Variables:Off ] + + <Run Termux:Tasker Plugin Command> + A51: Termux [ Configuration:%executable %arguments Timeout (Seconds):10 Continue Task After Error:On ] + + A52: Variable Set [ + Name:%command_failed + To:err = `%err` + + errmsg = + ``` + %errmsg + ``` + + exit_code = `%result` + + stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] If [ %err Set | %errmsg Set | %result neq 0 ] + + A53: If [ %command_failed Set ] + + <remove %err and %errmsg if not set> + A54: Variable Search Replace [ + Variable:%command_failed + Search:^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+ + Ignore Case:Off + Multi-Line:Off + One Match Only:Off + Store Matches In Array: + Replace Matches:On + Replace With: Continue Task After Error:On ] If [ %command_failed Set ] + + A55: Text Dialog [ + Title:Template 3 Command + Failed + Text:%command_failed + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A56: Stop [ + With Error:Off + Task: ] + + A57: Else + + A58: Text Dialog [ + Title:Template 3 Command Result + Text:stdout = + ``` + %stdout + ``` + + stderr = + ``` + %stderr + ``` + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A59: End If + + <Template 3 End> + A60: Anchor + + <Template 4 Start> + A61: Anchor + + <Goto "Return" + Enable this action to skip running this template> + A62: [X] Goto [ + Type:Action Label + Number:1 + Label:Return ] + + <Run `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` Command In Foreground> + A63: Anchor + + A64: Variable Clear [ + Name:%command_failed/%errmsg/%stdout/%stderr/%result + Pattern Matching:On + Local Variables Only:On + Clear All Variables:Off ] + + <Run Termux:Tasker Plugin Command> + A65: Termux [ Configuration:$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo s Timeout (Seconds):10 Continue Task After Error:On ] + + A66: Variable Set [ + Name:%command_failed + To:err = `%err` + + errmsg = + ``` + %errmsg + ``` + Recurse Variables:Off + Do Maths:Off + Append:Off + Max Rounding Digits:3 ] If [ %err Set | %errmsg Set ] + + A67: If [ %command_failed Set ] + + A68: Text Dialog [ + Title:Template 4 Command + Failed + Text:%command_failed + Button 1:OK + Button 2: + Button 3: + Close After (Seconds):30 + Use HTML:Off ] + + A69: Stop [ + With Error:Off + Task: ] + + A70: End If + + <Template 4 End> + A71: Anchor + + <Return> + A72: Anchor +`````` + +## +  + + +*This file was automatically generated using [tasker_config_utils v0.5.0](https://github.com/Taskomater/tasker_config_utils).* diff --git a/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.sha256sums b/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.sha256sums new file mode 100755 index 0000000..1ec4c51 --- /dev/null +++ b/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.sha256sums @@ -0,0 +1,2 @@ +9c8e0ddd9cdd5cb7699fd53a5d6271c20966dfbfbbca96a85fa5429e961eae59 Termux_Tasker_Plugin_Sudo_Templates.tsk.xml +56308e3dd1b042d01b67f98f2c92ad119ef1eb310c4d7f0edcd20bf83ecfc803 Termux_Tasker_Plugin_Sudo_Templates.tsk.md diff --git a/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.xml b/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.xml new file mode 100755 index 0000000..b85c653 --- /dev/null +++ b/templates/plugin_hosts/tasker/Termux_Tasker_Plugin_Sudo_Templates.tsk.xml @@ -0,0 +1,1073 @@ +<TaskerData sr="" dvi="1" tv="5.11.7.beta"> + <Task sr="task992"> + <cdate>1604448827988</cdate> + <edate>1607960952551</edate> + <id>992</id> + <nme>Termux Tasker Plugin Sudo Templates</nme> + <pri>100</pri> + <Share sr="Share"> + <d>A task that provides templates for running `sudo` script commands with the Termux:Tasker plugin in `superuser (root)` context in termux. This task requires Termux:Tasker version `>= 0.5`. Tasker must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file since the `$PREFIX/bin/sudo` absolute path is outside the `~/.termux/tasker/` directory, otherwise the plugin actions will fail. For android `>= 10`, Termux must also be granted `Draw Over Apps` permissions so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like `SuperSU` or `Magisk` for the `sudo` script to work. + +Check [Termux:Tasker Github](https://github.com/termux/termux-tasker) for more details on plugin configuration and variables and how to handle them. + +Check [sudo](https://github.com/agnostic-apollo/sudo) for more details for the `sudo` script. + + +Template 1 runs the `$PREFIX/bin/sudo dumpsys -l` command in the background as a template for the `path` `command_type` to list all android services. The args sent do not contain any quotes or special characters and can simply be sent, optionally surrounded with double quotes. + + +Template 2 runs the `$PREFIX/bin/sudo -s '%core_script' '%argument_1' '%argument_2'` command in the background as a template for the `script` `command_type` with `bash` as the `sudo shell`, which would be chosen automatically by default without having to pass the `--shell` option. The `termux_tasker_basic_bash_test` `bash` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain quotes or special characters, basically a literal string. The `-s` command option is passed to set `command_type` to `script`. The result is received back in the `%stdout` variable. The `sudo` executable is passed to the plugin using the `%executable` variable. The `%core_script`, `%argument_1` and `%argument_2` variables are used to store the dynamic values to be sent as `$1`, `$2` and `$3` respectively to `sudo` and are sent surrounded with single quotes instead of double quotes. They are first all set in the `%arguments` variable surrounded with single quotes and separated by a whitespace which is passed to the plugin. The `%core_script`, `%argument_1` and `%argument_2` variables may contain any type of characters, even a single quote, but single quotes must be escaped before the plugin action is run. To escape the single quotes, `Variable Search Replace` action is run to replace all single quotes `'` with one single quote, followed by one backslash, followed by two single quotes `'\''`. So `%argument_1` surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed to the executable as `$2`. The `Variable Search Replace` action must be used separately for each argument variable before adding it to the `%arguments` variable. Do not set multiple arguments in the same variable and use `Variable Search Replace` action on it since that will result in incorrect quoting. This template shows how you can dynamically create plugin commands at runtime using variables and send them to the plugin for execution, including passing the script text itself without having to create a physical file in `~/.termux/tasker/` directory. + + +Template 3 is almost the same as Template 2, but it runs the `$PREFIX/bin/sudo -s --shell=python '%core_script' '%argument_1' '%argument_2'` command in the background as a template for the `script` `command_type` with `python` as the `sudo shell`. The `termux_tasker_basic_python_test` `python` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain quotes or special characters, basically a literal string. The result is received back in the `%stdout` variable. + + +Template 4 runs `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` in the foreground as a template for the `su` `command_type` to start an interactive `bash` shell with priority to termux binaries and libraries. The `--shell-pre-commands` option is passed to run some commands before starting the interactive `bash` shell and its argument is passed surrounded with double quotes to prevent whitespace splitting. The `--title` option is passed to set the title of the terminal. Since commands will be run in a foreground terminal session, the `%stdout`, `%stderr` and `%result` variables will not be returned and only `%err` and `%errmsg` may be returned if the action fails. + + +The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. These shortcuts can be used in the `Executable` and the `Working Directory` plugin fields and in any path arguments to the `sudo` command. The scripts or binaries in the `~/.termux/tasker/` directory do not require them to prefixed with the full path, just set the name in the `Executable` field. + + +The `%command_failed` variable will be set if the plugin action failed, this is detected by whether `%err` or `%errmsg` is set by the plugin action or if `%result` does not equal `0` for background commands. If you run multiple plugin actions in the same task or are using `Local Variable Passthrough`, then you must clear the `%command_failed` variable and optionally the `%errmsg`, `%stdout`, `%stderr` and `%result` variables with the `Variable Clear` action before running each plugin action, in case they were already set, like by a previously failed plugin action after which the task was not stopped. + + +To debug arguments being passed or any errors, you can check `logcat` after increasing log level to `Debug`. Check `Debugging` section of `README.md` for more details. +## + + +**Parameters:** `-` + + +**Returns:** `-` + + +**Control:** + +``` +version_name: 0.1.0 +```</d> + <p>false</p> + <t></t> + </Share> + <Action sr="act0" ve="7"> + <code>300</code> + <label>A task that provides templates for running `sudo` script commands with the Termux:Tasker plugin in `superuser (root)` context in termux. This task requires Termux:Tasker version `>= 0.5`. Tasker must be granted `com.termux.permission.RUN_COMMAND` permission. The `sudo` script must be installed at `$PREFIX/bin/sudo`. The `allow-external-apps` property must also be set to `true` in `~/.termux/termux.properties` file since the `$PREFIX/bin/sudo` absolute path is outside the `~/.termux/tasker/` directory, otherwise the plugin actions will fail. For android `>= 10`, Termux must also be granted `Draw Over Apps` permissions so that foreground commands automatically start executing without the user having to manually click the `Termux` notification in the status bar dropdown notifications list for the commands to start. The device must be rooted and ideally `Termux` must have been granted root permissions by your root manager app like `SuperSU` or `Magisk` for the `sudo` script to work. + +Check [Termux:Tasker Github](https://github.com/termux/termux-tasker) for more details on plugin configuration and variables and how to handle them. + +Check [sudo](https://github.com/agnostic-apollo/sudo) for more details for the `sudo` script. + + +Template 1 runs the `$PREFIX/bin/sudo dumpsys -l` command in the background as a template for the `path` `command_type` to list all android services. The args sent do not contain any quotes or special characters and can simply be sent, optionally surrounded with double quotes. + + +Template 2 runs the `$PREFIX/bin/sudo -s '%core_script' '%argument_1' '%argument_2'` command in the background as a template for the `script` `command_type` with `bash` as the `sudo shell`, which would be chosen automatically by default without having to pass the `--shell` option. The `termux_tasker_basic_bash_test` `bash` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain quotes or special characters, basically a literal string. The `-s` command option is passed to set `command_type` to `script`. The result is received back in the `%stdout` variable. The `sudo` executable is passed to the plugin using the `%executable` variable. The `%core_script`, `%argument_1` and `%argument_2` variables are used to store the dynamic values to be sent as `$1`, `$2` and `$3` respectively to `sudo` and are sent surrounded with single quotes instead of double quotes. They are first all set in the `%arguments` variable surrounded with single quotes and separated by a whitespace which is passed to the plugin. The `%core_script`, `%argument_1` and `%argument_2` variables may contain any type of characters, even a single quote, but single quotes must be escaped before the plugin action is run. To escape the single quotes, `Variable Search Replace` action is run to replace all single quotes `'` with one single quote, followed by one backslash, followed by two single quotes `'\''`. So `%argument_1` surrounded with single quotes that would have been passed like `'some arg with single quote ' in it'` will be passed as `'some arg with single quote '\'' in it'`. This is basically 3 parts `'some arg with single quote '`, `\'` and `' in it'` but when processed, it will be considered as one single argument with the value `some arg with single quote ' in it` that is passed to the executable as `$2`. The `Variable Search Replace` action must be used separately for each argument variable before adding it to the `%arguments` variable. Do not set multiple arguments in the same variable and use `Variable Search Replace` action on it since that will result in incorrect quoting. This template shows how you can dynamically create plugin commands at runtime using variables and send them to the plugin for execution, including passing the script text itself without having to create a physical file in `~/.termux/tasker/` directory. + + +Template 3 is almost the same as Template 2, but it runs the `$PREFIX/bin/sudo -s --shell=python '%core_script' '%argument_1' '%argument_2'` command in the background as a template for the `script` `command_type` with `python` as the `sudo shell`. The `termux_tasker_basic_python_test` `python` script text is passed as the `core_script` argument with 2 complex dynamic args that may contain quotes or special characters, basically a literal string. The result is received back in the `%stdout` variable. + + +Template 4 runs `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` in the foreground as a template for the `su` `command_type` to start an interactive `bash` shell with priority to termux binaries and libraries. The `--shell-pre-commands` option is passed to run some commands before starting the interactive `bash` shell and its argument is passed surrounded with double quotes to prevent whitespace splitting. The `--title` option is passed to set the title of the terminal. Since commands will be run in a foreground terminal session, the `%stdout`, `%stderr` and `%result` variables will not be returned and only `%err` and `%errmsg` may be returned if the action fails. + + +The `$PREFIX/` is a shortcut for the termux prefix directory `/data/data/com.termux/files/usr/`. The `~/` is a shortcut for the termux home directory `/data/data/com.termux/files/home/`. These shortcuts can be used in the `Executable` and the `Working Directory` plugin fields and in any path arguments to the `sudo` command. The scripts or binaries in the `~/.termux/tasker/` directory do not require them to prefixed with the full path, just set the name in the `Executable` field. + + +The `%command_failed` variable will be set if the plugin action failed, this is detected by whether `%err` or `%errmsg` is set by the plugin action or if `%result` does not equal `0` for background commands. If you run multiple plugin actions in the same task or are using `Local Variable Passthrough`, then you must clear the `%command_failed` variable and optionally the `%errmsg`, `%stdout`, `%stderr` and `%result` variables with the `Variable Clear` action before running each plugin action, in case they were already set, like by a previously failed plugin action after which the task was not stopped. + + +To debug arguments being passed or any errors, you can check `logcat` after increasing log level to `Debug`. Check `Debugging` section of `README.md` for more details. +## + + +**Parameters:** `-` + + +**Returns:** `-` + + +**Control:** + +``` +version_name: 0.1.0 +```</label> + </Action> + <Action sr="act1" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%task_name</Str> + <Str sr="arg1" ve="3">Termux Tasker Plugin Sudo Templates</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act10" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 1 Command +Failed</Str> + <Str sr="arg2" ve="3">%command_failed</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act11" ve="7"> + <code>137</code> + <Int sr="arg0" val="0"/> + <Str sr="arg1" ve="3"/> + </Action> + <Action sr="act12" ve="7"> + <code>43</code> + </Action> + <Action sr="act13" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 1 Command Result</Str> + <Str sr="arg2" ve="3">stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act14" ve="7"> + <code>38</code> + </Action> + <Action sr="act15" ve="7"> + <code>300</code> + <label>Template 1 End</label> + </Action> + <Action sr="act16" ve="7"> + <code>300</code> + <label>Template 2 Start</label> + </Action> + <Action sr="act17" ve="7"> + <code>135</code> + <label>Goto "Template 3 Start" +Enable this action to skip running this template</label> + <on>false</on> + <Int sr="arg0" val="1"/> + <Int sr="arg1" val="1"/> + <Str sr="arg2" ve="3">Template 3 Start</Str> + </Action> + <Action sr="act18" ve="7"> + <code>300</code> + <label>Run `$PREFIX/bin/sudo -s '%core_script' '%argument_1' '%argument_2'` Command In Background</label> + </Action> + <Action sr="act19" ve="7"> + <code>547</code> + <label>set `$PREFIX/bin/sudo` to %executable</label> + <Str sr="arg0" ve="3">%executable</Str> + <Str sr="arg1" ve="3">$PREFIX/bin/sudo</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act2" ve="7"> + <code>300</code> + <label>Template 1 Start</label> + </Action> + <Action sr="act20" ve="7"> + <code>547</code> + <label>set `termux_tasker_basic_bash_test` script text to %core_script</label> + <Str sr="arg0" ve="3">%core_script</Str> + <Str sr="arg1" ve="3">#if parameter count is not 2 +if [ $# -ne 2 ]; then + echo "Invalid parameter count '$#' to 'termux_tasker_basic_bash_test'" 1>&2 + echo "$*" 1>&2 + exit 1 +fi + +echo "\$1=\`$1\`" +echo "\$2=\`$2\`" + +exit 0</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act21" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%argument_1</Str> + <Str sr="arg1" ve="3">json</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act22" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%argument_2</Str> + <Str sr="arg1" ve="3">{ + "name":"I'm Termux", + "license":"GPLv3", + "addons": { + "1":"Termux:API", + "2":"Termux:Boot", + "3":"Termux:Float", + "4":"Termux:Styling", + "5":"Termux:Tasker", + "6":"Termux:Widget" + } +}</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act23" ve="7"> + <code>598</code> + <label>replace all single quotes (') with ('\'')</label> + <Str sr="arg0" ve="3">%core_script</Str> + <Str sr="arg1" ve="3">'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="1"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">'\\''</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%core_script</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act24" ve="7"> + <code>598</code> + <label>replace all single quotes (') with ('\'')</label> + <Str sr="arg0" ve="3">%argument_1</Str> + <Str sr="arg1" ve="3">'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="1"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">'\\''</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%argument_1</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act25" ve="7"> + <code>598</code> + <label>replace all single quotes (') with ('\'')</label> + <Str sr="arg0" ve="3">%argument_2</Str> + <Str sr="arg1" ve="3">'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="1"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">'\\''</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%argument_2</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act26" ve="7"> + <code>547</code> + <label>set `-s '%core_script' '%argument_1' '%argument_2'` to %arguments</label> + <Str sr="arg0" ve="3">%arguments</Str> + <Str sr="arg1" ve="3">-s '%core_script' '%argument_1' '%argument_2'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act27" ve="7"> + <code>549</code> + <Str sr="arg0" ve="3">%command_failed/%errmsg/%stdout/%stderr/%result</Str> + <Int sr="arg1" val="1"/> + <Int sr="arg2" val="1"/> + <Int sr="arg3" val="0"/> + </Action> + <Action sr="act28" ve="7"> + <code>1256900802</code> + <label>Run Termux:Tasker Plugin Command</label> + <se>false</se> + <Bundle sr="arg0"> + <Vals sr="val"> + <com.termux.execute.arguments>%arguments</com.termux.execute.arguments> + <com.termux.execute.arguments-type>java.lang.String</com.termux.execute.arguments-type> + <com.termux.tasker.extra.EXECUTABLE>%executable</com.termux.tasker.extra.EXECUTABLE> + <com.termux.tasker.extra.EXECUTABLE-type>java.lang.String</com.termux.tasker.extra.EXECUTABLE-type> + <com.termux.tasker.extra.TERMINAL>false</com.termux.tasker.extra.TERMINAL> + <com.termux.tasker.extra.TERMINAL-type>java.lang.Boolean</com.termux.tasker.extra.TERMINAL-type> + <com.termux.tasker.extra.VERSION_CODE>4</com.termux.tasker.extra.VERSION_CODE> + <com.termux.tasker.extra.VERSION_CODE-type>java.lang.Integer</com.termux.tasker.extra.VERSION_CODE-type> + <com.termux.tasker.extra.WORKDIR></com.termux.tasker.extra.WORKDIR> + <com.termux.tasker.extra.WORKDIR-type>java.lang.String</com.termux.tasker.extra.WORKDIR-type> + <com.twofortyfouram.locale.intent.extra.BLURB>%executable %arguments</com.twofortyfouram.locale.intent.extra.BLURB> + <com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%stdout +Standard Output +The &lt;B&gt;stdout&lt;/B&gt; of the command.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1>%stderr +Standard Error +The &lt;B&gt;stderr&lt;/B&gt; of the command.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2>%result +Exit Code +The &lt;B&gt;exit code&lt;/B&gt; of the command. 0 often means success and anything else is usually a failure of some sort.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>com.termux.tasker.extra.EXECUTABLE com.termux.execute.arguments com.termux.tasker.extra.WORKDIR</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS> + <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type> + <net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled> + <net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">com.termux.tasker</Str> + <Str sr="arg2" ve="3">com.termux.tasker.EditConfigurationActivity</Str> + <Int sr="arg3" val="10"/> + </Action> + <Action sr="act29" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">err = `%err` + +errmsg = +``` +%errmsg +``` + +exit_code = `%result` + +stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + <ConditionList sr="if"> + <bool0>Or</bool0> + <bool1>Or</bool1> + <Condition sr="c0" ve="3"> + <lhs>%err</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c1" ve="3"> + <lhs>%errmsg</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c2" ve="3"> + <lhs>%result</lhs> + <op>1</op> + <rhs>0</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act3" ve="7"> + <code>135</code> + <label>Goto "Template 2 Start" +Enable this action to skip running this template</label> + <on>false</on> + <Int sr="arg0" val="1"/> + <Int sr="arg1" val="1"/> + <Str sr="arg2" ve="3">Template 2 Start</Str> + </Action> + <Action sr="act30" ve="7"> + <code>37</code> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs>\%err +\%errmsg</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act31" ve="7"> + <code>598</code> + <label>remove %err and %errmsg if not set</label> + <se>false</se> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3"/> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act32" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 2 Command +Failed</Str> + <Str sr="arg2" ve="3">%command_failed</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act33" ve="7"> + <code>137</code> + <Int sr="arg0" val="0"/> + <Str sr="arg1" ve="3"/> + </Action> + <Action sr="act34" ve="7"> + <code>43</code> + </Action> + <Action sr="act35" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 2 Command Result</Str> + <Str sr="arg2" ve="3">stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act36" ve="7"> + <code>38</code> + </Action> + <Action sr="act37" ve="7"> + <code>300</code> + <label>Template 2 End</label> + </Action> + <Action sr="act38" ve="7"> + <code>300</code> + <label>Template 3 Start</label> + </Action> + <Action sr="act39" ve="7"> + <code>135</code> + <label>Goto "Template 4 Start" +Enable this action to skip running this template</label> + <on>false</on> + <Int sr="arg0" val="1"/> + <Int sr="arg1" val="1"/> + <Str sr="arg2" ve="3">Template 4 Start</Str> + </Action> + <Action sr="act4" ve="7"> + <code>300</code> + <label>Run `$PREFIX/bin/sudo dumpsys -l` Command In Background</label> + </Action> + <Action sr="act40" ve="7"> + <code>300</code> + <label>Run `$PREFIX/bin/sudo -s --shell=python '%core_script' '%argument_1' '%argument_2'` Command In Background</label> + </Action> + <Action sr="act41" ve="7"> + <code>547</code> + <label>set `$PREFIX/bin/sudo` to %executable</label> + <Str sr="arg0" ve="3">%executable</Str> + <Str sr="arg1" ve="3">$PREFIX/bin/sudo</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act42" ve="7"> + <code>547</code> + <label>set `termux_tasker_basic_python_test` script text to %core_script</label> + <Str sr="arg0" ve="3">%core_script</Str> + <Str sr="arg1" ve="3">import sys + +argv_size = len(sys.argv) - 1 + +# if parameter count is not 2 +if argv_size != 2: + print("Invalid parameter count '%s' to 'termux_tasker_basic_python_test'" % argv_size, file=sys.stderr) + print("%s" % " ".join(sys.argv[1:]), file=sys.stderr) + sys.exit(1) + +print("$1=`%s`" % sys.argv[1]) +print("$2=`%s`" % sys.argv[2]) + +sys.exit(0)</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act43" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%argument_1</Str> + <Str sr="arg1" ve="3">json</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act44" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%argument_2</Str> + <Str sr="arg1" ve="3">{ + "name":"I'm Termux", + "license":"GPLv3", + "addons": { + "1":"Termux:API", + "2":"Termux:Boot", + "3":"Termux:Float", + "4":"Termux:Styling", + "5":"Termux:Tasker", + "6":"Termux:Widget" + } +}</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act45" ve="7"> + <code>598</code> + <label>replace all single quotes (') with ('\'')</label> + <Str sr="arg0" ve="3">%core_script</Str> + <Str sr="arg1" ve="3">'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="1"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">'\\''</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%core_script</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act46" ve="7"> + <code>598</code> + <label>replace all single quotes (') with ('\'')</label> + <Str sr="arg0" ve="3">%argument_1</Str> + <Str sr="arg1" ve="3">'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="1"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">'\\''</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%argument_1</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act47" ve="7"> + <code>598</code> + <label>replace all single quotes (') with ('\'')</label> + <Str sr="arg0" ve="3">%argument_2</Str> + <Str sr="arg1" ve="3">'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="1"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3">'\\''</Str> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%argument_2</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act48" ve="7"> + <code>547</code> + <label>set `-s --shell=python '%core_script' '%argument_1' '%argument_2'` to %arguments</label> + <Str sr="arg0" ve="3">%arguments</Str> + <Str sr="arg1" ve="3">-s --shell=python '%core_script' '%argument_1' '%argument_2'</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + </Action> + <Action sr="act49" ve="7"> + <code>549</code> + <Str sr="arg0" ve="3">%command_failed/%errmsg/%stdout/%stderr/%result</Str> + <Int sr="arg1" val="1"/> + <Int sr="arg2" val="1"/> + <Int sr="arg3" val="0"/> + </Action> + <Action sr="act5" ve="7"> + <code>549</code> + <Str sr="arg0" ve="3">%command_failed/%errmsg/%stdout/%stderr/%result</Str> + <Int sr="arg1" val="1"/> + <Int sr="arg2" val="1"/> + <Int sr="arg3" val="0"/> + </Action> + <Action sr="act50" ve="7"> + <code>1256900802</code> + <label>Run Termux:Tasker Plugin Command</label> + <se>false</se> + <Bundle sr="arg0"> + <Vals sr="val"> + <com.termux.execute.arguments>%arguments</com.termux.execute.arguments> + <com.termux.execute.arguments-type>java.lang.String</com.termux.execute.arguments-type> + <com.termux.tasker.extra.EXECUTABLE>%executable</com.termux.tasker.extra.EXECUTABLE> + <com.termux.tasker.extra.EXECUTABLE-type>java.lang.String</com.termux.tasker.extra.EXECUTABLE-type> + <com.termux.tasker.extra.TERMINAL>false</com.termux.tasker.extra.TERMINAL> + <com.termux.tasker.extra.TERMINAL-type>java.lang.Boolean</com.termux.tasker.extra.TERMINAL-type> + <com.termux.tasker.extra.VERSION_CODE>4</com.termux.tasker.extra.VERSION_CODE> + <com.termux.tasker.extra.VERSION_CODE-type>java.lang.Integer</com.termux.tasker.extra.VERSION_CODE-type> + <com.termux.tasker.extra.WORKDIR></com.termux.tasker.extra.WORKDIR> + <com.termux.tasker.extra.WORKDIR-type>java.lang.String</com.termux.tasker.extra.WORKDIR-type> + <com.twofortyfouram.locale.intent.extra.BLURB>%executable %arguments</com.twofortyfouram.locale.intent.extra.BLURB> + <com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%stdout +Standard Output +The &lt;B&gt;stdout&lt;/B&gt; of the command.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1>%stderr +Standard Error +The &lt;B&gt;stderr&lt;/B&gt; of the command.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2>%result +Exit Code +The &lt;B&gt;exit code&lt;/B&gt; of the command. 0 often means success and anything else is usually a failure of some sort.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>com.termux.tasker.extra.EXECUTABLE com.termux.execute.arguments com.termux.tasker.extra.WORKDIR</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS> + <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type> + <net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled> + <net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">com.termux.tasker</Str> + <Str sr="arg2" ve="3">com.termux.tasker.EditConfigurationActivity</Str> + <Int sr="arg3" val="10"/> + </Action> + <Action sr="act51" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">err = `%err` + +errmsg = +``` +%errmsg +``` + +exit_code = `%result` + +stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + <ConditionList sr="if"> + <bool0>Or</bool0> + <bool1>Or</bool1> + <Condition sr="c0" ve="3"> + <lhs>%err</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c1" ve="3"> + <lhs>%errmsg</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c2" ve="3"> + <lhs>%result</lhs> + <op>1</op> + <rhs>0</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act52" ve="7"> + <code>37</code> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs>\%err +\%errmsg</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act53" ve="7"> + <code>598</code> + <label>remove %err and %errmsg if not set</label> + <se>false</se> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3"/> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act54" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 3 Command +Failed</Str> + <Str sr="arg2" ve="3">%command_failed</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act55" ve="7"> + <code>137</code> + <Int sr="arg0" val="0"/> + <Str sr="arg1" ve="3"/> + </Action> + <Action sr="act56" ve="7"> + <code>43</code> + </Action> + <Action sr="act57" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 3 Command Result</Str> + <Str sr="arg2" ve="3">stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act58" ve="7"> + <code>38</code> + </Action> + <Action sr="act59" ve="7"> + <code>300</code> + <label>Template 3 End</label> + </Action> + <Action sr="act6" ve="7"> + <code>1256900802</code> + <label>Run Termux:Tasker Plugin Command</label> + <se>false</se> + <Bundle sr="arg0"> + <Vals sr="val"> + <com.termux.execute.arguments>dumpsys -l</com.termux.execute.arguments> + <com.termux.execute.arguments-type>java.lang.String</com.termux.execute.arguments-type> + <com.termux.tasker.extra.EXECUTABLE>$PREFIX/bin/sudo</com.termux.tasker.extra.EXECUTABLE> + <com.termux.tasker.extra.EXECUTABLE-type>java.lang.String</com.termux.tasker.extra.EXECUTABLE-type> + <com.termux.tasker.extra.TERMINAL>false</com.termux.tasker.extra.TERMINAL> + <com.termux.tasker.extra.TERMINAL-type>java.lang.Boolean</com.termux.tasker.extra.TERMINAL-type> + <com.termux.tasker.extra.VERSION_CODE>4</com.termux.tasker.extra.VERSION_CODE> + <com.termux.tasker.extra.VERSION_CODE-type>java.lang.Integer</com.termux.tasker.extra.VERSION_CODE-type> + <com.termux.tasker.extra.WORKDIR></com.termux.tasker.extra.WORKDIR> + <com.termux.tasker.extra.WORKDIR-type>java.lang.String</com.termux.tasker.extra.WORKDIR-type> + <com.twofortyfouram.locale.intent.extra.BLURB>$PREFIX/bin/sudo dumpsys -l</com.twofortyfouram.locale.intent.extra.BLURB> + <com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%stdout +Standard Output +The &lt;B&gt;stdout&lt;/B&gt; of the command.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1>%stderr +Standard Error +The &lt;B&gt;stderr&lt;/B&gt; of the command.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES1><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2>%result +Exit Code +The &lt;B&gt;exit code&lt;/B&gt; of the command. 0 often means success and anything else is usually a failure of some sort.</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES2></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>com.termux.tasker.extra.EXECUTABLE com.termux.execute.arguments com.termux.tasker.extra.WORKDIR</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS> + <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type> + <net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled> + <net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">com.termux.tasker</Str> + <Str sr="arg2" ve="3">com.termux.tasker.EditConfigurationActivity</Str> + <Int sr="arg3" val="10"/> + </Action> + <Action sr="act60" ve="7"> + <code>300</code> + <label>Template 4 Start</label> + </Action> + <Action sr="act61" ve="7"> + <code>135</code> + <label>Goto "Return" +Enable this action to skip running this template</label> + <on>false</on> + <Int sr="arg0" val="1"/> + <Int sr="arg1" val="1"/> + <Str sr="arg2" ve="3">Return</Str> + </Action> + <Action sr="act62" ve="7"> + <code>300</code> + <label>Run `$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su` Command In Foreground</label> + </Action> + <Action sr="act63" ve="7"> + <code>549</code> + <Str sr="arg0" ve="3">%command_failed/%errmsg/%stdout/%stderr/%result</Str> + <Int sr="arg1" val="1"/> + <Int sr="arg2" val="1"/> + <Int sr="arg3" val="0"/> + </Action> + <Action sr="act64" ve="7"> + <code>1256900802</code> + <label>Run Termux:Tasker Plugin Command</label> + <se>false</se> + <Bundle sr="arg0"> + <Vals sr="val"> + <com.termux.execute.arguments>--shell-pre-commands="echo 'starting sudo shell';" --title='sudo' su</com.termux.execute.arguments> + <com.termux.execute.arguments-type>java.lang.String</com.termux.execute.arguments-type> + <com.termux.tasker.extra.EXECUTABLE>$PREFIX/bin/sudo</com.termux.tasker.extra.EXECUTABLE> + <com.termux.tasker.extra.EXECUTABLE-type>java.lang.String</com.termux.tasker.extra.EXECUTABLE-type> + <com.termux.tasker.extra.TERMINAL>true</com.termux.tasker.extra.TERMINAL> + <com.termux.tasker.extra.TERMINAL-type>java.lang.Boolean</com.termux.tasker.extra.TERMINAL-type> + <com.termux.tasker.extra.VERSION_CODE>4</com.termux.tasker.extra.VERSION_CODE> + <com.termux.tasker.extra.VERSION_CODE-type>java.lang.Integer</com.termux.tasker.extra.VERSION_CODE-type> + <com.termux.tasker.extra.WORKDIR></com.termux.tasker.extra.WORKDIR> + <com.termux.tasker.extra.WORKDIR-type>java.lang.String</com.termux.tasker.extra.WORKDIR-type> + <com.twofortyfouram.locale.intent.extra.BLURB>$PREFIX/bin/sudo --shell-pre-commands="echo 'starting sudo s</com.twofortyfouram.locale.intent.extra.BLURB> + <com.twofortyfouram.locale.intent.extra.BLURB-type>java.lang.String</com.twofortyfouram.locale.intent.extra.BLURB-type> + <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS>com.termux.tasker.extra.EXECUTABLE com.termux.execute.arguments com.termux.tasker.extra.WORKDIR</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS> + <net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type>java.lang.String</net.dinglisch.android.tasker.extras.VARIABLE_REPLACE_KEYS-type> + <net.dinglisch.android.tasker.subbundled>true</net.dinglisch.android.tasker.subbundled> + <net.dinglisch.android.tasker.subbundled-type>java.lang.Boolean</net.dinglisch.android.tasker.subbundled-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">com.termux.tasker</Str> + <Str sr="arg2" ve="3">com.termux.tasker.EditConfigurationActivity</Str> + <Int sr="arg3" val="10"/> + </Action> + <Action sr="act65" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">err = `%err` + +errmsg = +``` +%errmsg +```</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + <ConditionList sr="if"> + <bool0>Or</bool0> + <Condition sr="c0" ve="3"> + <lhs>%err</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c1" ve="3"> + <lhs>%errmsg</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act66" ve="7"> + <code>37</code> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs>\%err +\%errmsg</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act67" ve="7"> + <code>377</code> + <Bundle sr="arg0"> + <Vals sr="val"> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES><StringArray sr=""><_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0>%td_button +Button +The label of the button that was clicked</_array_net.dinglisch.android.tasker.RELEVANT_VARIABLES0></StringArray></net.dinglisch.android.tasker.RELEVANT_VARIABLES> + <net.dinglisch.android.tasker.RELEVANT_VARIABLES-type>[Ljava.lang.String;</net.dinglisch.android.tasker.RELEVANT_VARIABLES-type> + </Vals> + </Bundle> + <Str sr="arg1" ve="3">Template 4 Command +Failed</Str> + <Str sr="arg2" ve="3">%command_failed</Str> + <Str sr="arg3" ve="3">OK</Str> + <Str sr="arg4" ve="3"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="30"/> + <Int sr="arg7" val="0"/> + </Action> + <Action sr="act68" ve="7"> + <code>137</code> + <Int sr="arg0" val="0"/> + <Str sr="arg1" ve="3"/> + </Action> + <Action sr="act69" ve="7"> + <code>38</code> + </Action> + <Action sr="act7" ve="7"> + <code>547</code> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">err = `%err` + +errmsg = +``` +%errmsg +``` + +exit_code = `%result` + +stdout = +``` +%stdout +``` + +stderr = +``` +%stderr +```</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Int sr="arg5" val="3"/> + <ConditionList sr="if"> + <bool0>Or</bool0> + <bool1>Or</bool1> + <Condition sr="c0" ve="3"> + <lhs>%err</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c1" ve="3"> + <lhs>%errmsg</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + <Condition sr="c2" ve="3"> + <lhs>%result</lhs> + <op>1</op> + <rhs>0</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act70" ve="7"> + <code>300</code> + <label>Template 4 End</label> + </Action> + <Action sr="act71" ve="7"> + <code>300</code> + <label>Return</label> + </Action> + <Action sr="act8" ve="7"> + <code>37</code> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs>\%err +\%errmsg</rhs> + </Condition> + </ConditionList> + </Action> + <Action sr="act9" ve="7"> + <code>598</code> + <label>remove %err and %errmsg if not set</label> + <se>false</se> + <Str sr="arg0" ve="3">%command_failed</Str> + <Str sr="arg1" ve="3">^err = `\%err`[\n]+errmsg =[\n]```[\n]\%errmsg[\n]```[\n]+</Str> + <Int sr="arg2" val="0"/> + <Int sr="arg3" val="0"/> + <Int sr="arg4" val="0"/> + <Str sr="arg5" ve="3"/> + <Int sr="arg6" val="1"/> + <Str sr="arg7" ve="3"/> + <ConditionList sr="if"> + <Condition sr="c0" ve="3"> + <lhs>%command_failed</lhs> + <op>12</op> + <rhs></rhs> + </Condition> + </ConditionList> + </Action> + </Task> +</TaskerData> diff --git a/tests/sudo_tests b/tests/sudo_tests new file mode 100755 index 0000000..4c2fadb --- /dev/null +++ b/tests/sudo_tests @@ -0,0 +1,1714 @@ +#!/data/data/com.termux/files/usr/bin/bash + +#title: sudo_tests +#description: a script to run automated tests for the sudo command types +#author: agnostic-apollo +#usage: run "sudo_tests --help" for detailed list of usages +#date: 14-Dec-2020 +#bash version: 4.1 or higher +#credits: + + + +SUDO_TESTS_HELP="$(cat<<'SUDO_TESTS_HELP_EOF' +sudo_tests is a script that run tests for the sudo script. + + +Usage: + sudo_tests [command_options] + sudo_tests [command_options] <su|asu|path|script> + sudo_tests [command_options] <su|asu|path|script> <shell> <test_number> + + +Available command_options: + [ -h | --help ] display this help screen + [ -v | -vv ] set verbose level to 1 or 2 + [ --help-extra ] display more help about how sudo_tests command works + + +Enabling verbose mode will fail test validation for tests that match +the output, its mainly for debugging. +SUDO_TESTS_HELP_EOF +)" + +SUDO_TESTS_HELP_EXTRA="$(cat<<'SUDO_TESTS_HELP_EXTRA' + +### Install and Usage Instructions For Termux In Android: + +#Install sudo and sudo_tests scripts and set ownership and executable permission +curl -L 'https://github.com/agnostic-apollo/sudo/raw/master/sudo' -o "/data/data/com.termux/files/usr/bin/sudo" +curl -L 'https://github.com/agnostic-apollo/sudo/raw/master/tests/sudo_tests' -o ~/sudo_tests +or +cat "/storage/emulated/0/Download/sudo" > "/data/data/com.termux/files/usr/bin/sudo" +cat "/storage/emulated/0/Download/sudo_tests" > ~/sudo_tests + +export termux_bin_path="/data/data/com.termux/files/usr/bin"; export owner="$(stat -c "%u" "$termux_bin_path")"; chown "$owner:$owner" "$termux_bin_path/sudo" && chmod 700 "$termux_bin_path/sudo"; chown "$owner:$owner" ~/sudo_tests && chmod 700 ~/sudo_tests; + + +#Install shells used by sudo_tests script +pkg install bash zsh dash fish python ruby nodejs perl lua52 lua53 lua54 php rlwrap + + +#Install termux_tasker_basic_bash_test and termux_tasker_basic_python_test script and set permissions +mkdir -p /data/data/com.termux/files/home/.termux/tasker +chmod 700 -R /data/data/com.termux/files/home/.termux + +curl -L 'https://github.com/termux/termux-tasker/raw/master/templates/scripts/termux_tasker_basic_bash_test' -o "/data/data/com.termux/files/home/.termux/tasker/termux_tasker_basic_bash_test" +curl -L 'https://github.com/termux/termux-tasker/raw/master/templates/scripts/termux_tasker_basic_python_test' -o "/data/data/com.termux/files/home/.termux/tasker/termux_tasker_basic_python_test" +or +cat "/storage/emulated/0/Download/termux_tasker_basic_bash_test" > "/data/data/com.termux/files/home/.termux/tasker/termux_tasker_basic_bash_test" +cat "/storage/emulated/0/Download/termux_tasker_basic_python_test" > "/data/data/com.termux/files/home/.termux/tasker/termux_tasker_basic_python_test" + +chmod 700 "/data/data/com.termux/files/home/.termux/tasker/termux_tasker_basic_bash_test" +chmod 700 "/data/data/com.termux/files/home/.termux/tasker/termux_tasker_basic_python_test" + + +#Install youtube-dl + +apt install curl ffmpeg python +curl -L https://yt-dl.org/downloads/latest/youtube-dl -o /data/data/com.termux/files/usr/bin/youtube-dl +chmod 700 /data/data/com.termux/files/usr/bin/youtube-dl + +#Fix shebang, must be run on install and on each update +#otherwise will get "bad interpreter: /usr/bin/env" errors when running with root shell, plugin or TermuxCommand, +#because termux-exec doesn't work for those cases and LD_PRELOAD isn't set +#but its not required for usage with sudo since it sets the LD_PRELOAD +termux-fix-shebang /data/data/com.termux/files/usr/bin/youtube-dl +# +#Update youtube-dl +youtube-dl -U + + +#Install bandcamp-dl +pip3 install bandcamp-downloader +# +#Update bandcamp-dl +pkg install git +pip install git+https://github.com/iheanyi/bandcamp-dl --upgrade + + + +#Following are not required for the tests currently + +#Install pry, the ruby interactive shell alternative +gem install pry + +#Install python2 +pkg install python2 + +#Install ksh +apt install unstable-repo +pkg intsall loksh + + + +#Follow instructions in `RC File Variables` section of README.md to fix `rc` files. + +#Finally run tests + +#Run all tests +bash ~/sudo_tests + +#Run only script command type tests +bash ~/sudo_tests script + +#Run only script command type bash shell test 1 +bash ~/sudo_tests script bash 1 + +#Run only script command type bash shell test 1 with verbose level 2 +bash ~/sudo_tests -vv script bash 1 + +#For some tests **sometimes** where *-stdin-string option is used to pass a string to an interactive shell using stdin, +#the stdout is returned in an unsynchronized manner which fails the validation, possibly due to magisk su or maybe an android 10 issue +#The tests run fine with SuperSU v2.82 on android 7 without issues. Currently not sure of the real reason +#If that happens, just rerun the test and it should ideally work +#This is also the reason why su and asu tests use matches instead of equality checks, some script type tests still use exact matches + +SUDO_TESTS_HELP_EXTRA +)" + + + +### Set Default Variables Start +sudo_tests_verbose_level=2 #default to log level 2 +sudo_tests_args_verbose_level=0 #set this to 1 manually, if you want to debug arguments received + +SUDO_TESTS_TEMP_DIRECTORY_SUFFIX="" #default to none + +declare -ga SUDO_TESTS_DIRECTORIES=() +declare -ga sudo_v_args=() + +#set regexes for validation +valid_number_regex='^[0-9]+$' +valid_absolute_path_regex='^(/[^/]+)+$' + +### Set Default Variables + + +[[ x"${BASH_SOURCE[0]}" == x"$0" ]] && sudo_tests_exit_command="exit" || sudo_tests_exit_command="return" + +function sudo_tests_log () { local log_level="${1}"; shift; if [[ $sudo_tests_verbose_level -ge $log_level ]]; then echo "$@"; fi } +function sudo_tests_log_literal() { local log_level="${1}"; shift; if [[ $sudo_tests_verbose_level -ge $log_level ]]; then echo -e "$@"; fi } +function sudo_tests_log_errors () { echo "$@" 1>&2; } +function sudo_tests_log_args() { if [[ $sudo_tests_args_verbose_level -ge "1" ]]; then echo "$@"; fi } +function sudo_tests_log_arg_errors() { echo "$@" 1>&2; } + +#stdin in closed for some tests as a patch to SuperSU automatic redirection of stderr to stdout when running in interactive mode +function close_stdin () { if [ -t 0 ]; then exec <&-; fi } +function reopen_stdin () { if [[ "$tty_path" == "/dev/"* ]]; then exec <$tty_path; fi } + + +sudo_tests_main() { + + local return_value + + #source the sudo script + if [ ! -x /data/data/com.termux/files/usr/bin/sudo ]; then + sudo_tests_log_errors "Failed to find \"/data/data/com.termux/files/usr/bin/sudo\"" + $sudo_tests_exit_command 1 + else + source /data/data/com.termux/files/usr/bin/sudo + sudo_set_default_variables + fi + + local -a SUDO_TESTS_REQUIRED_VARIABLES_ARRAY=( + TERMUX_FILES + TERMUX_PREFIX + TERMUX_HOME + TERMUX_PATH + TERMUX_LD_LIBRARY_PATH + ANDROID_PATH + ) + + local var + + for var in "${SUDO_TESTS_REQUIRED_VARIABLES_ARRAY[@]}"; do + #if var is not set + if [ -z "${!var}" ]; then + sudo_tests_log_errors "The required variable \"$var\" for sudo_tests is not set" + $sudo_tests_exit_command 1 + fi + done + + + #process the command or options passed to sudo_tests + process_sudo_tests_parameters "$@" + + + sudo_tests_run + $sudo_tests_exit_command $? + +} + +sudo_tests_run() { + + local return_value + + #set SUDO_TESTS_WORKING_DIR to use for tests + SUDO_TESTS_WORKING_DIR="$TERMUX_HOME" + + cd "$SUDO_TESTS_WORKING_DIR" + + if [ -t 0 ]; then + tty_path="$(tty 2>/dev/null)" + fi + + #find machine arch + ARCH="$(uname -m)" + return_value=$? + if [ $return_value -ne 0 ]; then + sudo_tests_log_errors "Failed to find machine arch" + return $return_value + fi + + #find android sdk/os version + ANDROID_SDK_VERSION="$(getprop "ro.build.version.sdk")" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$ANDROID_SDK_VERSION" =~ $valid_number_regex ]]; then + sudo_tests_log_errors "Failure while finding \"ro.build.version.sdk\" property" + sudo_tests_log_errors "ANDROID_SDK_VERSION = \"$ANDROID_SDK_VERSION\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + + + #generate unique suffix to use for sudo_tests directories + SUDO_TESTS_TEMP_DIRECTORY_SUFFIX="$(mktemp -d --dry-run sudo.XXXXXX)" + return_value=$? + if [ $return_value -ne 0 ] || [[ ! "$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" =~ ^sudo\.......$ ]]; then + sudo_tests_log_errors "Failure while running mktemp to create SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" + sudo_tests_log_errors "SUDO_TESTS_TEMP_DIRECTORY_SUFFIX=\"$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX\"" + if [ $return_value -eq 0 ]; then + return_value=1 + fi + return $return_value + fi + + export SUDO_SHELL_HOME="$SUDO_TESTS_WORKING_DIR/.shell_home.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" + export SUDO_POST_SHELL_HOME="$SUDO_TESTS_WORKING_DIR/.post_shell_home.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" + + SUDO_SHELL_HOME_BASENAME="${SUDO_SHELL_HOME##*/}" #strip longest match of */ from start + SUDO_POST_SHELL_HOME_BASENAME="${SUDO_POST_SHELL_HOME##*/}" #strip longest match of */ from start + + SUDO_TESTS_DIRECTORIES+=("$SUDO_SHELL_HOME") + SUDO_TESTS_DIRECTORIES+=("$SUDO_POST_SHELL_HOME") + + + + #set traps to run commands before exiting sudo_tests + trap 'sudo_tests_cleanup' EXIT + trap 'sudo_tests_cleanup TERM' TERM + trap 'sudo_tests_cleanup INT' INT + trap 'sudo_tests_cleanup HUP' HUP + trap 'sudo_tests_cleanup QUIT' QUIT + + + + #run tests + if [ -z "$sudo_tests_command_type" ]; then + sudo_tests_log 0 "Running 'sudo' tests" + sudo_tests_log 0 $'\n\n'; sudo_tests_run_su_tests || return $? + sudo_tests_log 0 $'\n\n'; sudo_tests_run_asu_tests || return $? + sudo_tests_log 0 $'\n\n'; sudo_tests_run_path_tests || return $? + sudo_tests_log 0 $'\n\n'; sudo_tests_run_script_tests || return $? + elif [[ "$sudo_tests_command_type" != *,* ]] && [[ ",su,asu,path,script," == *",$sudo_tests_command_type,"* ]]; then + sudo_tests_run_"$sudo_tests_command_type"_tests || return $? + else + sudo_tests_log_errors "Unknown tests command type $sudo_tests_command_type" + show_sudo_tests_help + return 1 + fi + + sudo_tests_log 0 $'\n\n'"All 'sudo' tests successful" + + return 0 + +} + +sudo_tests_run_su_tests() { + + local return_value + + sudo_tests_command_type="su" + sudo_tests_log 0 "Running '$sudo_tests_command_type' command type tests" + + + + #Run bash tests + + set_sudo_tests_current_test_shell "bash" + shell="bash" + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" --dry-run --shell-stdin-string='cd "$HOME"; pwd; exit;' su)" + validate_sudo_test 0 $? "$output" "" + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" --shell-stdin-string='cd "$HOME"; pwd; exit;' su)" + validate_sudo_test 0 $? "$output" "*" "$SUDO_SHELL_HOME" "*" + + run_sudo_test 3 && \ + output="$(sudo "${sudo_v_args[@]}" --shell-stdin-string='echo "$PATH"; exit;' su)" + validate_sudo_test 0 $? "$output" "*" "$TERMUX_PATH" "*" + + run_sudo_test 4 && \ + output="$(sudo "${sudo_v_args[@]}" --shell-stdin-string='echo "$LD_LIBRARY_PATH"; exit;' su)" + validate_sudo_test 0 $? "$output" "*" "$TERMUX_LD_LIBRARY_PATH" "*" + + + + sudo_tests_log 0 $'\n\n'"All 'su' command type tests successful" + + return 0 + +} + +sudo_tests_run_asu_tests() { + + local return_value + + sudo_tests_command_type="asu" + sudo_tests_log 0 "Running '$sudo_tests_command_type' command type tests" + + + + #Run bash tests + + set_sudo_tests_current_test_shell "bash" + shell="bash" + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" --dry-run --shell-stdin-string='cd "$HOME"; pwd; exit;' asu)" + validate_sudo_test 0 $? "$output" "" + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" --shell-stdin-string='cd "$HOME"; pwd; exit;' asu)" + validate_sudo_test 0 $? "$output" "*" "$SUDO_SHELL_HOME" "*" + + run_sudo_test 3 && \ + output="$(sudo "${sudo_v_args[@]}" --shell-stdin-string='echo "$PATH"; exit;' asu)" + validate_sudo_test 0 $? "$output" "*" "$ANDROID_PATH" "*" + + run_sudo_test 4 && \ + output="$(sudo "${sudo_v_args[@]}" --shell-stdin-string='echo "$LD_LIBRARY_PATH"; exit;' asu)" + return_value=$? + #if ARCH is 64 bit + if [[ "$ARCH" == *64 ]]; then + validate_sudo_test 0 $return_value "$output" "*" "/system/lib64:/system/lib:" "*" + else + validate_sudo_test 0 $return_value "$output" "*" "/system/lib:" "*" + fi + + + + sudo_tests_log 0 $'\n\n'"All 'asu' command type tests successful" + + return 0 +} + +sudo_tests_run_path_tests() { + + local return_value + + sudo_tests_command_type="path" + sudo_tests_log 0 "Running '$sudo_tests_command_type' command type tests" + + + + #Run bash tests + + set_sudo_tests_current_test_shell "bash" + shell="bash" + + if [ -x "$TERMUX_HOME/.termux/tasker/termux_tasker_basic_bash_test" ]; then + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" "$TERMUX_HOME/.termux/tasker/termux_tasker_basic_bash_test" "$shell" "shell")" + validate_sudo_test 0 $? "$output" "\$1=\`$shell\`"$'\n'"\$2=\`shell\`" + else + skip_sudo_test 1 + fi + + if [ -x "$TERMUX_HOME/.termux/tasker/termux_tasker_basic_bash_test" ]; then + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" '~/.termux/tasker/termux_tasker_basic_bash_test' "$shell" "shell")" + validate_sudo_test 0 $? "$output" "\$1=\`$shell\`"$'\n'"\$2=\`shell\`" + else + skip_sudo_test 2 + fi + + if [ -x "$TERMUX_HOME/.termux/tasker/termux_tasker_basic_bash_test" ]; then + run_sudo_test 3 && \ + output="$(cd "$TERMUX_HOME"; sudo "${sudo_v_args[@]}" './.termux/tasker/termux_tasker_basic_bash_test' "$shell" "shell")" + validate_sudo_test 0 $? "$output" "\$1=\`$shell\`"$'\n'"\$2=\`shell\`" + else + skip_sudo_test 3 + fi + + if [ -x "$TERMUX_HOME/.termux/tasker/termux_tasker_basic_bash_test" ]; then + run_sudo_test 4 && \ + output="$(cd "$TERMUX_HOME/.termux/tasker"; sudo "${sudo_v_args[@]}" 'termux_tasker_basic_bash_test' "$shell" "shell")" + validate_sudo_test 0 $? "$output" "\$1=\`$shell\`"$'\n'"\$2=\`shell\`" + else + skip_sudo_test 4 + fi + + + + run_sudo_test 5 && \ + output="$(sudo "${sudo_v_args[@]}" ps -e -o cmd | grep "$TERMUX_PREFIX/bin/ps")" + validate_sudo_test 0 $? "$output" "*" "$TERMUX_PREFIX/bin/ps -e -o cmd" "*" + + + + run_sudo_test 6 && \ + output="$(sudo "${sudo_v_args[@]}" dumpsys activity services | grep -E 'ServiceRecord\{.*com\.termux\/.*\.app\.TermuxService\}')" + validate_sudo_test 0 $? "$output" "*" "com.termux/.app.TermuxService" "*" + + + + run_sudo_test 7 && \ + output="$(sudo "${sudo_v_args[@]}" -r echo "$COMMA_ALTERNATIVE")" + validate_sudo_test 0 $? "$output" "," + + + + sudo_tests_log 0 $'\n\n'"All 'path' command type tests successful" + + return 0 + +} + +sudo_tests_run_script_tests() { + + local return_value + + sudo_tests_command_type="script" + sudo_tests_log 0 "Running '$sudo_tests_command_type' command type tests" + + + + ### Run bash tests + + set_sudo_tests_current_test_shell "bash" + shell="bash" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --dry-run 'cd "$HOME"; pwd;')" + validate_sudo_test 0 $? "$output" "" + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s 'cd "$HOME"; pwd;')" + validate_sudo_test 0 $? "$output" "$SUDO_SHELL_HOME" + + run_sudo_test 3 && \ + output="$(cd "$SUDO_TESTS_WORKING_DIR"; sudo "${sudo_v_args[@]}" -s --shell-home="./$SUDO_SHELL_HOME_BASENAME" 'cd "$HOME"; pwd;')" + validate_sudo_test 0 $? "$output" "$SUDO_SHELL_HOME" + + run_sudo_test 4 && \ + output="$(cd "$SUDO_TESTS_WORKING_DIR"; sudo "${sudo_v_args[@]}" -s --shell-home="$SUDO_SHELL_HOME_BASENAME" 'cd "$HOME"; pwd;')" + validate_sudo_test 0 $? "$output" "$SUDO_SHELL_HOME" + + run_sudo_test 5 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell-home="$SUDO_POST_SHELL_HOME" --post-shell-stdin-string='exit' --post-shell-pre-commands='cd "$HOME"; pwd;')" + validate_sudo_test 0 $? "$output" "" "$SUDO_POST_SHELL_HOME" "*" + + + + run_sudo_test 6 && \ + output="$(sudo "${sudo_v_args[@]}" -s 'readlink -f /proc/$$/exe')" + validate_sudo_test 0 $? "$output" "$TERMUX_PREFIX/bin/bash" + + run_sudo_test 7 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$TERMUX_PREFIX/bin/bash" 'readlink -f /proc/$$/exe')" + validate_sudo_test 0 $? "$output" "$TERMUX_PREFIX/bin/bash" + + run_sudo_test 8 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell-stdin-string='readlink -f /proc/$$/exe; exit;')" + validate_sudo_test 0 $? "$output" '# readlink -f /proc/$$/exe; exit;'$'\n'"$TERMUX_PREFIX/bin/bash"$'\n'"exit" + + run_sudo_test 9 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell="$TERMUX_PREFIX/bin/bash" --post-shell-stdin-string='readlink -f /proc/$$/exe; exit;')" + validate_sudo_test 0 $? "$output" '# readlink -f /proc/$$/exe; exit;'$'\n'"$TERMUX_PREFIX/bin/bash"$'\n'"exit" + + + + run_sudo_test 10 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +echo "Hi, $1 $2." +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 11 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" ' +echo '\''What is your name?'\'' +read name +echo "Hi, $name." +' + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + + + run_sudo_test 12 && \ + output="$(sudo "${sudo_v_args[@]}" -sd --shell="$shell" ' +echo '\''What is your name?'\'' +read name +echo "Hi, $name." +' + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, ." + + + + run_sudo_test 13 && \ + output="$(sudo "${sudo_v_args[@]}" -so --shell="$shell" --shell-stdin-string="$shell" ' +echo '\''What is your name?'\'' +read name +echo "Hi, $name." 1>&2 +' + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + + + run_sudo_test 14 && \ + output="$(sudo "${sudo_v_args[@]}" -sf 'echo "Running $1 $2 script: $(basename "$0")"' "$shell" "shell")" + validate_sudo_test 0 $? "$output" "Running $shell shell script: sudo_core_script" + + + run_sudo_test 15 && \ + output="$(sudo "${sudo_v_args[@]}" -sf --script-name="sudo_core_script_custom" 'echo "Running $1 $2 script: $(basename "$0")"' "$shell" "shell")" + validate_sudo_test 0 $? "$output" "Running $shell shell script: sudo_core_script_custom" + + + + if [ -x "$TERMUX_HOME/.termux/tasker/termux_tasker_basic_bash_test" ]; then + run_sudo_test 16 && \ + output="$(sudo "${sudo_v_args[@]}" -sF '~/.termux/tasker/termux_tasker_basic_bash_test' "$shell" "shell")" + validate_sudo_test 0 $? "$output" "\$1=\`$shell\`"$'\n'"\$2=\`shell\`" + else + skip_sudo_test 16 + fi + + + if [ -x "$TERMUX_PREFIX/bin/tasker_config_utils" ]; then + run_sudo_test 17 && \ + output="$(sudo "${sudo_v_args[@]}" -s --script-name="tasker_config_utils" <(cat "$TERMUX_PREFIX/bin/tasker_config_utils") --help 1>/dev/null)" + validate_sudo_test 0 $? "$output" + else + skip_sudo_test 17 + fi + + + if [ -x "$TERMUX_PREFIX/bin/tasker_config_utils" ]; then + run_sudo_test 18 && \ + output="$(sudo "${sudo_v_args[@]}" -sf --script-name="tasker_config_utils" <(cat "$TERMUX_PREFIX/bin/tasker_config_utils") --help 1>/dev/null)" + validate_sudo_test 0 $? "$output" + else + skip_sudo_test 18 + fi + + + + + + run_sudo_test 19 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell-pre-commands=' +export VARIABLE_1="VARIABLE_VALUE_1" +export VARIABLE_2="VARIABLE_VALUE_2" +' ' +echo "Hi, $1 $2." +echo "VARIABLE_1=\`$VARIABLE_1\`" +echo "VARIABLE_2=\`$VARIABLE_2\`" +' "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell."$'\n'"VARIABLE_1=\`VARIABLE_VALUE_1\`"$'\n'"VARIABLE_2=\`VARIABLE_VALUE_2\`" + + + + run_sudo_test 20 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell-stdin-string='exit' --shell-pre-commands='echo "Start script"' --shell-post-commands='echo "Exited script"' --post-shell-pre-commands='echo "Starting interactive shell"' --post-shell-post-commands='echo "Exited interactive shell"' 'echo "Hi, $1 $2."' "$shell" "shell")" + validate_sudo_test 0 $? "$output" "Start script"$'\n'"Hi, $shell shell."$'\n'"Exited script"$'\n'"Starting interactive shell"$'\n'"# exit"$'\n'"exit"$'\n'"Exited interactive shell" + + + + + + sudo_test_temp_shell_home="/shell_home.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" + SUDO_TESTS_DIRECTORIES+=("$sudo_test_temp_shell_home") + run_sudo_test 21 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell-home="$sudo_test_temp_shell_home" 'cd "$HOME"; pwd;' 2>&1)" + return_value=$? + # If android >= 10 + if [ "$ANDROID_SDK_VERSION" -ge 29 ]; then + validate_sudo_test 1 $return_value "$output" "" "The rootfs \"/\" partition cannot be mounted as rw to be used for SUDO_SHELL_HOME \"$sudo_test_temp_shell_home\"" "*" + else + validate_sudo_test 0 $return_value "$output" "$sudo_test_temp_shell_home" + fi + + + + sudo_test_temp_post_shell_home="/post_shell_home.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" + SUDO_TESTS_DIRECTORIES+=("$sudo_test_temp_post_shell_home") + run_sudo_test 22 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell-home="$sudo_test_temp_post_shell_home" --post-shell-stdin-string='exit' --post-shell-pre-commands='cd "$HOME"; pwd;' 2>&1)" + return_value=$? + # If android >= 10 + if [ "$ANDROID_SDK_VERSION" -ge 29 ]; then + validate_sudo_test 1 $return_value "$output" "" "The rootfs \"/\" partition cannot be mounted as rw to be used for SUDO_POST_SHELL_HOME \"$sudo_test_temp_post_shell_home\"" "*" + else + validate_sudo_test 0 $return_value "$output" "$sudo_test_temp_post_shell_home"$'\n'"# exit"$'\n'"exit" + fi + + + + sudo_test_temp_shell_home="/system/shell_home.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" + SUDO_TESTS_DIRECTORIES+=("$sudo_test_temp_shell_home") + run_sudo_test 23 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell-home="$sudo_test_temp_shell_home" 'cd "$HOME"; pwd;' 2>&1)" + return_value=$? + # If android >= 10 + if [ "$ANDROID_SDK_VERSION" -ge 29 ]; then + validate_sudo_test 1 $return_value "$output" "" "The system \"/system\" partition cannot be mounted as rw to be used for SUDO_SHELL_HOME \"$sudo_test_temp_shell_home\"" "*" + else + validate_sudo_test 0 $return_value "$output" "$sudo_test_temp_shell_home" + fi + + + + sudo_test_temp_post_shell_home="/system/post_shell_home.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" + SUDO_TESTS_DIRECTORIES+=("$sudo_test_temp_post_shell_home") + run_sudo_test 24 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell-home="$sudo_test_temp_post_shell_home" --post-shell-stdin-string='exit' --post-shell-pre-commands='cd "$HOME"; pwd;' 2>&1)" + return_value=$? + # If android >= 10 + if [ "$ANDROID_SDK_VERSION" -ge 29 ]; then + validate_sudo_test 1 $return_value "$output" "" "The system \"/system\" partition cannot be mounted as rw to be used for SUDO_POST_SHELL_HOME \"$sudo_test_temp_post_shell_home\"" "*" + else + validate_sudo_test 0 $return_value "$output" "$sudo_test_temp_post_shell_home"$'\n'"# exit"$'\n'"exit" + fi + + + + + sudo_test_temp_work_dir="$SUDO_TESTS_WORKING_DIR/work_dir.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" + SUDO_TESTS_DIRECTORIES+=("$sudo_test_temp_work_dir") + + run_sudo_test 25 && \ + output="$(sudo "${sudo_v_args[@]}" -s --work-dir="$sudo_test_temp_work_dir" 'pwd' 2>&1)" + return_value=$? + validate_sudo_test 0 $return_value "$output" "$sudo_test_temp_work_dir" + + run_sudo_test 26 && \ + output="$(sudo "${sudo_v_args[@]}" -s --work-dir='~/work_dir.'"$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" 'pwd' 2>&1)" + return_value=$? + validate_sudo_test 0 $return_value "$output" "$sudo_test_temp_work_dir" + + run_sudo_test 27 && \ + output="$(cd "$SUDO_TESTS_WORKING_DIR"; sudo "${sudo_v_args[@]}" -s --work-dir="./work_dir.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" 'pwd' 2>&1)" + return_value=$? + validate_sudo_test 0 $return_value "$output" "$sudo_test_temp_work_dir" + + run_sudo_test 28 && \ + output="$(cd "$SUDO_TESTS_WORKING_DIR"; sudo "${sudo_v_args[@]}" -s --work-dir="work_dir.$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" 'pwd' 2>&1)" + return_value=$? + validate_sudo_test 0 $return_value "$output" "$sudo_test_temp_work_dir" + + + + + + #redirect stderr to stdout, same as `--script-redirect=0` + close_stdin + run_sudo_test 29 && \ + sudo_tests_run_subshell_command stdout stderr sudo "${sudo_v_args[@]}" -so --shell="$shell" 'echo stdout; echo stderr 1>&2' + return_value=$? + reopen_stdin + validate_sudo_test 0 $return_value "$stdout" "stdout"$'\n'"stderr" + validate_sudo_test 0 $return_value "$stderr" "" + + #redirect stdout to stderr, same as `--script-redirect=1` + close_stdin + run_sudo_test 30 && \ + sudo_tests_run_subshell_command stdout stderr sudo "${sudo_v_args[@]}" -sO --shell="$shell" 'echo stdout; echo stderr 1>&2' + return_value=$? + reopen_stdin + validate_sudo_test 0 $return_value "$stdout" "" + validate_sudo_test 0 $return_value "$stderr" "stdout"$'\n'"stderr" + + #redirect redirect stdout to /dev/null + close_stdin + run_sudo_test 31 && \ + sudo_tests_run_subshell_command stdout stderr sudo "${sudo_v_args[@]}" -s --script-redirect=2 --shell="$shell" 'echo stdout; echo stderr 1>&2' + return_value=$? + reopen_stdin + validate_sudo_test 0 $return_value "$stdout" "" + validate_sudo_test 0 $return_value "$stderr" "stderr" + + #redirect stderr to /dev/null for core_script, same as `--script-redirect=3` + close_stdin + run_sudo_test 32 && \ + sudo_tests_run_subshell_command stdout stderr sudo "${sudo_v_args[@]}" -sn --shell="$shell" 'echo stdout; echo stderr 1>&2' + return_value=$? + reopen_stdin + validate_sudo_test 0 $return_value "$stdout" "stdout" + validate_sudo_test 0 $return_value "$stderr" "" + + #redirect both stdout and stderr to /dev/null, same as `--script-redirect=4` + close_stdin + run_sudo_test 33 && \ + sudo_tests_run_subshell_command stdout stderr sudo "${sudo_v_args[@]}" -sN --shell="$shell" 'echo stdout; echo stderr 1>&2' + return_value=$? + reopen_stdin + validate_sudo_test 0 $return_value "$stdout" "" + validate_sudo_test 0 $return_value "$stderr" "" + + #redirect stderr to stdout and redirect stdout to stderr + close_stdin + run_sudo_test 34 && \ + sudo_tests_run_subshell_command stdout stderr sudo "${sudo_v_args[@]}" -s --script-redirect=5 --shell="$shell" 'echo stdout; echo stderr 1>&2' + return_value=$? + reopen_stdin + validate_sudo_test 0 $return_value "$stdout" "stderr" + validate_sudo_test 0 $return_value "$stderr" "stdout" + + #redirect stderr to stdout and redirect stdout to /dev/null + close_stdin + run_sudo_test 35 && \ + sudo_tests_run_subshell_command stdout stderr sudo "${sudo_v_args[@]}" -s --script-redirect=6 --shell="$shell" 'echo stdout; echo stderr 1>&2' + return_value=$? + reopen_stdin + validate_sudo_test 0 $return_value "$stdout" "stderr" + validate_sudo_test 0 $return_value "$stderr" "" + + + + close_stdin + run_sudo_test 36 && \ + sudo_tests_run_subshell_command stdout stderr sudo "${sudo_v_args[@]}" -sie --post-shell-stdin-string='exit' --shell="$shell" <(cat <<'SUDO_EOF' +#if parameter count is not 2 +if [ $# -ne 2 ]; then + echo "Invalid parameter count '$#' to 'termux_tasker_basic_bash_test'" 1>&2 + echo "$*" 1>&2 + exit 1 +fi + +echo "\$1=\`$1\`" +echo "\$2=\`$2\`" + +exit 0 +SUDO_EOF +) "$shell" + return_value=$? + reopen_stdin + validate_sudo_test 1 $return_value "$stdout" "" + validate_sudo_test 1 $return_value "$stderr" "Invalid parameter count '1' to 'termux_tasker_basic_bash_test'"$'\n'"$shell" + + + + + + run_sudo_test 37 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell=bas 'echo "Hi, $1 $2."' "$shell" "shell" 2>&1)" + validate_sudo_test 1 $? "$output" "" "The SUDO_SHELL \"bas\" is not supported. It must be one of" "*" + + + + run_sudo_test 38 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell=bas 'echo "Hi, $1 $2."' "$shell" "shell" 2>&1)" + validate_sudo_test 1 $? "$output" "" "The SUDO_POST_SHELL \"bas\" is not supported. It must be one of" "*" + + + + run_sudo_test 39 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell-home='~/.config' 'echo "Hi, $1 $2."' "$shell" "shell" 2>&1)" + validate_sudo_test 1 $? "$output" "" "The \"$TERMUX_HOME/.config\" cannot be used as SUDO_SHELL_HOME" "*" + + + + run_sudo_test 40 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell-home='$PREFIX/' 'echo "Hi, $1 $2."' "$shell" "shell" 2>&1)" + validate_sudo_test 1 $? "$output" "" "The \"$TERMUX_PREFIX\" cannot be used as SUDO_SHELL_HOME" "*" + + + + run_sudo_test 41 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell-home='$PREFIX/home' 'echo "Hi, $1 $2."' "$shell" "shell" 2>&1)" + validate_sudo_test 1 $? "$output" "" "The \"$TERMUX_PREFIX/home\" cannot be used as SUDO_SHELL_HOME" "*" + + + + run_sudo_test 42 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell-home='~/.config' 'echo "Hi, $1 $2."' "$shell" "shell" 2>&1)" + validate_sudo_test 1 $? "$output" "" "The \"$TERMUX_HOME/.config\" cannot be used as SUDO_POST_SHELL_HOME" "*" + + + + run_sudo_test 43 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell-home='$PREFIX/' 'echo "Hi, $1 $2."' "$shell" "shell" 2>&1)" + validate_sudo_test 1 $? "$output" "" "The \"$TERMUX_PREFIX\" cannot be used as SUDO_POST_SHELL_HOME" "*" + + + + run_sudo_test 44 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell-home='$PREFIX/home' 'echo "Hi, $1 $2."' "$shell" "shell" 2>&1)" + validate_sudo_test 1 $? "$output" "" "The \"$TERMUX_PREFIX/home\" cannot be used as SUDO_POST_SHELL_HOME" "*" + + + + + + run_sudo_test 45 && \ + output="$(sudo "${sudo_v_args[@]}" -sr "[[ \",\" == \"$COMMA_ALTERNATIVE\" ]] && [[ "\$1" == \"$COMMA_ALTERNATIVE\" ]] && echo \"arg1 equals simple comma\"" "$COMMA_ALTERNATIVE")" + validate_sudo_test 0 $? "$output" "arg1 equals simple comma" + + + + run_sudo_test 46 && \ + output="$(sudo "${sudo_v_args[@]}" -si --post-shell="python" --post-shell-stdin-string='exit(0)' 2>&1)" + validate_sudo_test 0 $? "$output" "*" "Python" "*" + + + + + + ### Run zsh tests + if [ -x "$TERMUX_PREFIX/bin/zsh" ]; then + + set_sudo_tests_current_test_shell "zsh" + shell="zsh" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" 'readlink -f /proc/$$/exe')" + validate_sudo_test 0 $? "$output" "$TERMUX_PREFIX/bin/zsh" + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +echo "Hi, $1 $2." +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 3 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +echo "What is your name?" +read name +echo "Hi, $name." +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping zsh tests since its not installed" + fi + + + + + + ### Run dash tests + if [ -x "$TERMUX_PREFIX/bin/dash" ]; then + + set_sudo_tests_current_test_shell "dash" + shell="dash" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +echo "Hi, $1 $2." +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +echo "What is your name?" +read name +echo "Hi, $name." +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping dash tests since its not installed" + fi + + + + + + ### Run sh tests + if [ -x "$TERMUX_PREFIX/bin/sh" ]; then + + set_sudo_tests_current_test_shell "sh" + shell="sh" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +echo "Hi, $1 $2." +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +echo "What is your name?" +read name +echo "Hi, $name." +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping sh tests since its not installed" + fi + + + + + + ### Run fish tests + if [ -x "$TERMUX_PREFIX/bin/fish" ]; then + + set_sudo_tests_current_test_shell "fish" + shell="fish" + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" 'readlink -f /proc/$fish_pid/exe')" + validate_sudo_test 0 $? "$output" "$TERMUX_PREFIX/bin/fish" + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +echo "Hi, $argv[1] $argv[2]." +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 3 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +echo "What is your name?" +read -p "" name +echo "Hi, $name." +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping fish tests since its not installed" + fi + + + + + + ### Run python tests + if [ -x "$TERMUX_PREFIX/bin/python" ]; then + + set_sudo_tests_current_test_shell "python" + shell="python" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +import sys +print("Hi, %s %s." % (sys.argv[1], sys.argv[2])) +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +name = input("What is your name?\n") +print("Hi, %s." % name) +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + + if [ -x "$TERMUX_HOME/.termux/tasker/termux_tasker_basic_python_test" ]; then + run_sudo_test 3 && \ + output="$(sudo "${sudo_v_args[@]}" '~/.termux/tasker/termux_tasker_basic_python_test' "$shell" "shell")" + validate_sudo_test 0 $? "$output" "\$1=\`$shell\`"$'\n'"\$2=\`shell\`" + else + skip_sudo_test 3 + fi + + if [ -x "$TERMUX_PREFIX/bin/youtube-dl" ]; then + run_sudo_test 4 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell=python --script-name="youtube-dl" <(cat "$TERMUX_PREFIX/bin/youtube-dl") --help 1>/dev/null)" + validate_sudo_test 0 $? "$output" + else + skip_sudo_test 4 + fi + + if [ -x "$TERMUX_PREFIX/bin/youtube-dl" ]; then + run_sudo_test 5 && \ + output="$(sudo "${sudo_v_args[@]}" -sF --shell=python "$TERMUX_PREFIX/bin/youtube-dl" --help 1>/dev/null)" + validate_sudo_test 0 $? "$output" + else + skip_sudo_test 5 + fi + + if [ -x "$TERMUX_PREFIX/bin/bandcamp-dl" ]; then + run_sudo_test 6 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell=python "$(cat "$TERMUX_PREFIX/bin/bandcamp-dl")" --help)" + validate_sudo_test 0 $? "$output" + else + skip_sudo_test 6 + fi + + if [ -x "$TERMUX_PREFIX/bin/bandcamp-dl" ]; then + run_sudo_test 7 && \ + output="$(sudo "${sudo_v_args[@]}" -s --script-decode --shell=python "$(cat "$TERMUX_PREFIX/bin/bandcamp-dl" | base64)" --help)" + validate_sudo_test 0 $? "$output" + else + skip_sudo_test 7 + fi + + else + sudo_tests_log 1 "Skipping python tests since its not installed" + fi + + + + + + ### Run ruby tests + if [ -x "$TERMUX_PREFIX/bin/ruby" ]; then + + set_sudo_tests_current_test_shell "ruby" + shell="ruby" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +puts "Hi, " + ARGV[0] + " " + ARGV[1] + "." +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +puts "What is your name?" +name = STDIN.gets +name = '' if name.nil? +puts "Hi, " + name.chomp.to_s + "." +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping ruby tests since its not installed" + fi + + + + + + ### Run node tests + if [ -x "$TERMUX_PREFIX/bin/node" ]; then + + set_sudo_tests_current_test_shell "node" + shell="node" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +console.log(`Hi, ${process.argv[2]} ${process.argv[3]}.`) +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +const readline = require('readline').createInterface({ + input: process.stdin, + output: process.stdout +}) + +readline.question(`What is your name?\n`, (name) => { + console.log(`Hi, ${name}.`) + readline.close() +}) +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "*" "Hi, $shell." "" + + else + sudo_tests_log 1 "Skipping node tests since its not installed" + fi + + + + + + ### Run perl tests + if [ -x "$TERMUX_PREFIX/bin/perl" ]; then + + set_sudo_tests_current_test_shell "perl" + shell="perl" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +print "Hi, ", $ARGV[0], " ", $ARGV[1], ".\n"; +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +print "What is your name?\n"; +$name = <STDIN>; +chomp($name); +print "Hi, ", $name, ".\n"; +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping perl tests since its not installed" + fi + + + + + + ### Run lua5.2 tests + if [ -x "$TERMUX_PREFIX/bin/lua5.2" ]; then + + set_sudo_tests_current_test_shell "lua5.2" + shell="lua5.2" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +io.write('Hi, ', arg[1], ' ', arg[2], '.\n') +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +io.write('What is your name?\n') +local name = io.read() +io.write('Hi, ', name, '.\n') +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping lua5.2 tests since its not installed" + fi + + + + + + ### Run lua5.3 tests + if [ -x "$TERMUX_PREFIX/bin/lua5.3" ]; then + + set_sudo_tests_current_test_shell "lua5.3" + shell="lua5.3" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +io.write('Hi, ', arg[1], ' ', arg[2], '.\n') +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +io.write('What is your name?\n') +local name = io.read() +io.write('Hi, ', name, '.\n') +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping lua5.3 tests since its not installed" + fi + + + + + + ### Run lua5.4 tests + if [ -x "$TERMUX_PREFIX/bin/lua5.4" ]; then + + set_sudo_tests_current_test_shell "lua5.4" + shell="lua5.4" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +io.write('Hi, ', arg[1], ' ', arg[2], '.\n') +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +io.write('What is your name?\n') +local name = io.read() +io.write('Hi, ', name, '.\n') +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping lua5.4 tests since its not installed" + fi + + + + + + ### Run php tests + if [ -x "$TERMUX_PREFIX/bin/php" ]; then + + set_sudo_tests_current_test_shell "php" + shell="php" + + + run_sudo_test 1 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" <(cat <<'SUDO_EOF' +<?php +echo "Hi, " . $argv[1] . " " . $argv[2] . ".\n"; +SUDO_EOF +) "$shell" "shell" + )" + validate_sudo_test 0 $? "$output" "Hi, $shell shell." + + + + run_sudo_test 2 && \ + output="$(sudo "${sudo_v_args[@]}" -s --shell="$shell" --shell-stdin-string="$shell" <(cat <<'SUDO_EOF' +<?php +echo "What is your name?\n"; +$name = readline(); +echo "Hi, " . $name . ".\n"; +SUDO_EOF +) + )" + validate_sudo_test 0 $? "$output" "What is your name?"$'\n'"php"$'\n'"Hi, $shell." + + else + sudo_tests_log 1 "Skipping php tests since its not installed" + fi + + + + sudo_tests_log 0 $'\n\n'"All 'script' command type tests successful" + + return 0 + +} + +#capture stdout and stderr of command in separate variables +#sudo_tests_run_subshell_command stdout_variable_name stderr_variable_name command [arguments] +sudo_tests_run_subshell_command() { + + local return_value + + local stdout_variable_name="$1"; + local stderr_variable_name="$2" + shift 2 + + local valid_bash_variable_name_regex='^[a-zA-Z][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + #if stdout_variable_name does equals "stdout_variable_name" and or is not a valid bash variable_name + if [[ "$stdout_variable_name" == "stdout_variable_name" ]]|| [[ ! "$stdout_variable_name" =~ $valid_bash_variable_name_regex ]]; then + sudo_tests_log_errors "stdout_variable_name \"$1\" passed to \"sudo_tests_run_subshell_command\" equals \"stdout_variable_name\" or is not a valid bash variable name" + return 1 + fi + + #if stderr_variable_name does equals "stderr_variable_name" and or is not a valid bash variable_name + if [[ "$stderr_variable_name" == "stderr_variable_name" ]]|| [[ ! "$stderr_variable_name" =~ $valid_bash_variable_name_regex ]]; then + sudo_tests_log_errors "stderr_variable_name \"$1\" passed to \"sudo_tests_run_subshell_command\" equals \"stderr_variable_name\" or is not a valid bash variable name" + return 1 + fi + + unset "$stdout_variable_name" + unset "$stderr_variable_name" + + #credits madmurphy https://stackoverflow.com/a/59592881/14686958 + + #some_command is launched + + #we then have some_command's stdout on the descriptor 1, + #some_command's stderr on the descriptor 2 and + #some_command's exit code redirected to the descriptor 3 + + #stdout is piped to tr (sanitization) to remove NUL + + #stderr is swapped with stdout (using temporarily the descriptor 4) and piped to tr (sanitization) + + #the exit code (descriptor 3) is swapped with stderr (now descriptor 1) and piped to exit $(cat) + + #stderr (now descriptor 3) is redirected to the descriptor 1, end expanded as the second argument of printf + + #the exit code of exit $(cat) is captured by the third argument of printf + + #the output of printf is redirected to the descriptor 2, where stdout was already present + + #the concatenation of stdout and the output of printf is piped to read + + { + IFS=$'\n' read -r -d '' "$stdout_variable_name"; + IFS=$'\n' read -r -d '' "$stderr_variable_name"; + (IFS=$'\n' read -r -d '' return_value; return $return_value); + } < <((printf '\0%s\0%d\0' "$(((({ "$@"; echo "$?" 1>&3-; } | tr -d '\0' 1>&4-) 4>&2- 2>&1- | tr -d '\0' 1>&4-) 3>&1- | exit "$(cat)") 4>&1-)" "$?" 1>&2) 2>&1) + +} + +set_sudo_tests_current_test_shell () { + + sudo_tests_previous_temp_test_shell="$sudo_tests_current_test_shell"; + sudo_tests_current_test_shell="$1" + +} + +set_sudo_tests_current_test_number () { + + #to handle the case when sudo_tests_user_test_number was the last test of a shell + if [[ ! -z "$sudo_tests_previous_temp_test_shell" ]]; then + sudo_tests_previous_test_shell="$sudo_tests_previous_temp_test_shell" + sudo_tests_previous_temp_test_shell="" + else + sudo_tests_previous_test_shell="$sudo_tests_current_test_shell" + fi + + sudo_tests_previous_test_number="$sudo_tests_current_test_number"; + sudo_tests_current_test_number="$1"; + + #if sudo_tests_user_test_shell and sudo_tests_user_test_number are set + #and + #sudo_tests_previous_test_shell equals sudo_tests_user_test_shell + #and + #sudo_tests_previous_test_number equals sudo_tests_user_test_number + #then exit + if [ ! -z "$sudo_tests_user_test_shell" ] && [ ! -z "$sudo_tests_user_test_number" ] && \ + [[ "$sudo_tests_previous_test_shell" == "$sudo_tests_user_test_shell" ]] && \ + [[ "$sudo_tests_previous_test_number" == "$sudo_tests_user_test_number" ]]; then + $sudo_tests_exit_command 0 + fi + + #if sudo_tests_user_test_shell and sudo_tests_user_test_number are set + #and + #sudo_tests_current_test_shell does not equal sudo_tests_user_test_shell + # or + #sudo_tests_current_test_number does not equal sudo_tests_user_test_number + #then skip the current test + if [ ! -z "$sudo_tests_user_test_shell" ] && [ ! -z "$sudo_tests_user_test_number" ] && \ + ([[ "$sudo_tests_current_test_shell" != "$sudo_tests_user_test_shell" ]] || \ + [[ "$sudo_tests_current_test_number" != "$sudo_tests_user_test_number" ]]); then + return 112 + else + sudo_tests_run_test=1 + return 0 + fi + +} + +#run_sudo_test current_test_number +run_sudo_test () { + + set_sudo_tests_current_test_number "$1" || return $? + sudo_tests_log 1 $'\n\n'"Running $sudo_tests_current_test_shell shell test $sudo_tests_current_test_number"; + +} + +#skip_sudo_test current_test_number +skip_sudo_test () { + + set_sudo_tests_current_test_number "$1" || return $? + sudo_tests_log 1 $'\n\n'"Skipping $sudo_tests_current_test_shell shell test $sudo_tests_current_test_number"; + +} + +#validate_sudo_test expected_result_value actual_result_value actual_output expected_output +#validate_sudo_test expected_result_value actual_result_value actual_output expected_output_prefix_glob expected_output expected_output_suffix_glob +validate_sudo_test () { + + local return_value + + if [[ "$sudo_tests_run_test" != "1" ]]; then + return 0 + fi + + local expected_result_value="$1" + local actual_result_value="$2" + + #remove all escape sequences and strip all characters except tab(11), linefeed(12) and characters in range 40-176 + local actual_output="$(sed 's/\x1b\[[0-9;]*[a-zA-Z]//g' <<< "$3" | tr -cd '\11\12\40-\176')" + + local expected_output + local output_match_test_failed=0 + + #if actual_output is set + if [ ! -z "$actual_output" ]; then + cat -v <<<"$actual_output" + fi + + #if parameter count equals 4 and actual_output does not exactly match $4 + if [ $# -eq 4 ] && [[ "$actual_output" != "$4" ]]; then + output_match_test_failed=1 + expected_output="$4" + fi + + #if parameter count greater than 4 and actual_output does not exactly match $5 with $4 and $6 as optional globs + if [ $# -gt 4 ] && [[ "$actual_output" != $4"$5"$6 ]]; then + output_match_test_failed=1 + expected_output="$4$5$6" + fi + + #if output_match_test_failed is enabled + if [[ "$output_match_test_failed" == "1" ]]; then + sudo_tests_log_errors $'\n'"$sudo_tests_current_test_shell shell test $sudo_tests_current_test_number for '$sudo_tests_command_type' command type failed since expected_output does not match actual_output" + sudo_tests_log_errors $'\n'"expected_output=\`$expected_output\`" + sudo_tests_log_errors $'\n'"actual_output=\`$actual_output\`" + $sudo_tests_exit_command 1 + fi + + #if actual_result_value is not a valid exit code + if [[ ! "$actual_result_value" =~ $valid_number_regex ]]; then + sudo_tests_log_errors $'\n'"$sudo_tests_current_test_shell shell test $sudo_tests_current_test_number for '$sudo_tests_command_type' command type exit code '$actual_result_value' is invalid" + $sudo_tests_exit_command 1 + fi + + #if expected_result_value is not a valid exit code + if [[ ! "$expected_result_value" =~ $valid_number_regex ]]; then + sudo_tests_log_errors $'\n'"$sudo_tests_current_test_shell shell test $sudo_tests_current_test_number for '$sudo_tests_command_type' command type exit code '$expected_result_value' is invalid" + $sudo_tests_exit_command 1 + fi + + #if actual_result_value does not equal expected_result_value + if [[ "$actual_result_value" != "$expected_result_value" ]]; then + sudo_tests_log_errors $'\n'"$sudo_tests_current_test_shell shell test $sudo_tests_current_test_number for '$sudo_tests_command_type' command type failed with exit code $actual_result_value but expected exit code was $expected_result_value" + $sudo_tests_exit_command "$actual_result_value" + fi + + sudo_tests_log 1 $'\n'"Test successful"; + + return 0 + +} + +sudo_tests_cleanup() { + + #the sudo_tests_cleanup will do the following: + #remove the EXIT trap so its not called again + #remove temp_directory if set + #if a signal argument was passed, then remove its trap and + #then exit with the same signal + #so that parent processes can be notified if necessary + + local sudo_tests_exit_code=$? + trap - EXIT + sudo_tests_remove_sudo_tests_temp_directories + [ -n "$1" ] && trap - $1; exit $sudo_tests_exit_code; + +} + +sudo_tests_remove_sudo_tests_temp_directories() { + + #set SU and SU_BIN_PATH + #redirecting stderr to /dev/null since the following would ideally also fail on the first test run + sudo_set_su_variables &>/dev/null || return $? + + #if SU, SUDO_TESTS_DIRECTORIES and SUDO_TESTS_TEMP_DIRECTORY_SUFFIX are set + if [ ! -z "$SU" ] && [ ${#SUDO_TESTS_DIRECTORIES[@]} -ne 0 ] && [ ! -z "$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" ]; then + + sudo_tests_log 2 $'\n\n'"Running sudo_tests_remove_sudo_tests_temp_directories" + + rootfs_partition_dependent_paths="" + system_partition_dependent_paths="" + + #set rootfs_partition_dependent_paths and system_partition_dependent_paths + #which are required by the sudo_set_su_env_command function to add '--mount-master option' to SU_ENV_COMMAND + + for i in "${!SUDO_TESTS_DIRECTORIES[@]}"; do + SUDO_TESTS_DIRECTORY="${SUDO_TESTS_DIRECTORIES[$i]}" + + #find the parent directory of the SUDO_TESTS_DIRECTORY + SUDO_TESTS_DIRECTORY_BASENAME="${SUDO_TESTS_DIRECTORY##*/}" #strip longest match of */ from start + SUDO_TESTS_DIRECTORY_PARENT_DIR="${SUDO_TESTS_DIRECTORY:0:${#SUDO_TESTS_DIRECTORY} - ${#SUDO_TESTS_DIRECTORY_BASENAME}}" #substring from 0 to position of basename + case $SUDO_TESTS_DIRECTORY_PARENT_DIR in *[!/]*/) SUDO_TESTS_DIRECTORY_PARENT_DIR=${SUDO_TESTS_DIRECTORY_PARENT_DIR%"${SUDO_TESTS_DIRECTORY_PARENT_DIR##*[!/]}"};; *[/]) SUDO_TESTS_DIRECTORY_PARENT_DIR="/";; esac #remove trailing slashes if not root + + #if SUDO_TESTS_DIRECTORY_PARENT_DIR is in rootfs "/" partition + if [[ "$SUDO_TESTS_DIRECTORY_PARENT_DIR" =~ $valid_path_in_rootfs_partition_regex ]]; then + rootfs_partition_dependent_paths+=" \"$SUDO_TESTS_DIRECTORY\"" + fi + + #if SUDO_TESTS_DIRECTORY is in system "/system" partition + if [[ "$SUDO_TESTS_DIRECTORY" =~ $valid_path_in_system_partition_regex ]]; then + system_partition_dependent_paths+=" \"$SUDO_TESTS_DIRECTORY\"" + fi + done + + + # If android < 10 + if [[ "$ANDROID_SDK_VERSION" =~ $valid_number_regex ]] && [ "$ANDROID_SDK_VERSION" -lt 29 ]; then + #if rootfs_partition_dependent_paths is set + if [ ! -z "$rootfs_partition_dependent_paths" ]; then + #remount android rootfs partition as rw + sudo_tests_log 2 "Remounting rootfs \"/\" partition as rw to be used for$rootfs_partition_dependent_paths" + sudo_remount_partition "rootfs" "rw" 0 + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 112 ]; then + sudo_tests_log_errors "Failed to mount rootfs \"/\" partition as rw to be used for$rootfs_partition_dependent_paths" + return $return_value + fi + fi + + #if system_partition_dependent_paths is set + if [ ! -z "$system_partition_dependent_paths" ]; then + #remount android system partition as rw + sudo_tests_log 2 "Remounting system \"/system\" partition as rw to be used for$system_partition_dependent_paths" + sudo_remount_partition "system" "rw" 0 + return_value=$? + if [ $return_value -ne 0 ] && [ $return_value -ne 112 ]; then + sudo_tests_log_errors "Failed to mount system \"/system\" partition as rw to be used for$system_partition_dependent_paths" + return $return_value + fi + fi + fi + + + #remove all directories in SUDO_TESTS_DIRECTORIES if they match the SUDO_TESTS_TEMP_DIRECTORY_SUFFIX + for i in "${!SUDO_TESTS_DIRECTORIES[@]}"; do + if [[ "${SUDO_TESTS_DIRECTORIES[$i]}" == *"$SUDO_TESTS_TEMP_DIRECTORY_SUFFIX" ]]; then + "$SU" --mount-master -c "[ -d \"${SUDO_TESTS_DIRECTORIES[$i]}\" ] && rm -rf \"${SUDO_TESTS_DIRECTORIES[$i]}\"" + fi + done + + + # If android < 10 + if [[ "$ANDROID_SDK_VERSION" =~ $valid_number_regex ]] && [ "$ANDROID_SDK_VERSION" -lt 29 ]; then + #if rootfs_partition_dependent_paths is set + if [ ! -z "$rootfs_partition_dependent_paths" ]; then + sudo_remount_partition "rootfs" "ro" "0" + fi + + #if system_partition_dependent_paths is set + if [ ! -z "$system_partition_dependent_paths" ]; then + sudo_remount_partition "system" "ro" "0" + fi + fi + + fi + + return 0 + +} + +process_sudo_tests_parameters() { + + #parse options to sudo_tests command + while getopts ":hv-:" opt; do + case ${opt} in + -) + long_optargs="${OPTARG#*=}" + case "${OPTARG}" in + help-extra) + sudo_tests_log_args "Parsing option: '--${OPTARG%=*}'" + show_sudo_tests_help_extra + $sudo_tests_exit_command 0 + ;; + help-extra*) + sudo_tests_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_tests_on_error + ;; + help) + sudo_tests_log_args "Parsing option: '--${OPTARG%=*}'" + show_sudo_tests_help + $sudo_tests_exit_command 0 + ;; + help*) + sudo_tests_log_arg_errors "Invalid option or parameters not allowed for option: '--${OPTARG%=*}'" + exit_sudo_tests_on_error + ;; + '' ) #"--" terminates argument processing to support non-options that start with dashes + sudo_tests_log_args "Parsing option: '--'" + break + ;; + *) + sudo_tests_log_arg_errors "Unknown option '--${OPTARG%=*}'" + exit_sudo_tests_on_error + ;; + esac + ;; + h) + sudo_tests_log_args "Parsing option: '-${opt}'" + show_sudo_tests_help + $sudo_tests_exit_command 0 + ;; + v) + sudo_tests_log_args "Parsing option: '-${opt}'" + #sudo_verbose_level would have been sourced from the sudo script + if [ "$sudo_verbose_level" -lt "2" ]; then + sudo_verbose_level=$((sudo_verbose_level+1)); + sudo_v_args+=("-v") + else + sudo_tests_log_arg_errors "Invalid Option, max verbose level is 2" + exit_sudo_tests_on_error + fi + ;; + \?) + sudo_tests_log_arg_errors "Unknown option: '-${OPTARG}'" + exit_sudo_tests_on_error + ;; + esac + done + shift $((OPTIND -1)) #remove already processed arguments from argument list + + sudo_tests_command_type="$1" + sudo_tests_user_test_shell="$2" + sudo_tests_user_test_number="$3" + +} + +show_sudo_tests_help() { + + cat<<<"$SUDO_TESTS_HELP" + +} + +show_sudo_tests_help_extra() { + + show_sudo_tests_help + + cat<<<"$SUDO_TESTS_HELP_EXTRA" + +} + +exit_sudo_tests_on_error() { + + show_sudo_tests_help + $sudo_tests_exit_command 1 + +} + +#call sudo_tests_main function +[[ x"${BASH_SOURCE[0]}" == x"$0" ]] && sudo_tests_main "$@"; $sudo_tests_exit_command 0;