Go to file
HOLZSCHUCH Nicolas 6a83eb1c6c Fixed a few bugs, added jsc.system() command. 2023-04-18 17:12:29 +02:00
.github/workflows Upodated workflow 2022-05-21 09:09:57 +02:00
Resources Updated scp/sftp commands, options for ssh 2021-04-20 17:02:44 +02:00
awk Continuing cleanup. Re-created all Info.plist and header files 2020-07-06 21:55:45 +02:00
awk-23.30.1 Multiple bug fixes, better management of environment variables for 2022-11-09 17:38:40 +01:00
awk-awk-35 Newer version of awk 2023-04-14 22:09:03 +02:00
bsd_diff Multiple bug fixes, better management of environment variables for 2022-11-09 17:38:40 +01:00
bsd_find Multiple bug fixes, better management of environment variables for 2022-11-09 17:38:40 +01:00
curl Continuing cleanup. Re-created all Info.plist and header files 2020-07-06 21:55:45 +02:00
curl_ios Adding curl_ios to repo 2017-12-09 16:21:07 +01:00
curl_ios.xcodeproj Moving everyone back into single framework 2017-12-09 00:07:33 +01:00
curl_ios_static Static curl framework for blink 2017-12-09 16:19:41 +01:00
curl_ios_static.xcodeproj Fix curl_ios_static 2021-01-18 15:39:14 +03:00
file_cmds Multiple changes stderr -> thread_stderr, bug fix in ls 2020-11-24 19:05:20 +01:00
file_cmds_ios Moving everyone back into single framework 2017-12-09 00:07:33 +01:00
files Continuing cleanup. Re-created all Info.plist and header files 2020-07-06 21:55:45 +02:00
ios_system Merge branch 'master' of github.com:holzschu/ios_system 2022-11-09 15:30:35 +01:00
ios_system.xcodeproj Updated project file 2022-05-21 09:06:35 +02:00
less-34 mutex locks and atomic counters to prevent issues when multiple commands 2021-10-05 19:13:26 +02:00
libarchive Multiple bug fixes, better management of environment variables for 2022-11-09 17:38:40 +01:00
libarchive_ios Moving everyone back into single framework 2017-12-09 00:07:33 +01:00
libinfo Adding source for libinfo to repository 2020-07-06 22:02:28 +02:00
libutil Adding libutil as source instead of patch 2020-07-06 22:03:46 +02:00
shell Continuing cleanup. Re-created all Info.plist and header files 2020-07-06 21:55:45 +02:00
shell_cmds Bug fixing in env and date 2023-04-14 22:08:00 +02:00
shell_cmds_ios Fixed a few bugs, added jsc.system() command. 2023-04-18 17:12:29 +02:00
ssh_cmd Side effects of moving to frameworks 2018-03-25 22:45:14 +02:00
ssh_keygen Avoid an issue when pthread_self() is reset before the end of 2023-02-04 19:47:45 +01:00
tar Continuing cleanup. Re-created all Info.plist and header files 2020-07-06 21:55:45 +02:00
text Continuing cleanup. Re-created all Info.plist and header files 2020-07-06 21:55:45 +02:00
text_cmds Multiple bug fixes, better management of environment variables for 2022-11-09 17:38:40 +01:00
text_cmds_grp removed alt-space 2020-07-06 21:56:42 +02:00
wasm3@d460452679 Commit before merge: wasm3 2021-08-09 18:38:09 +02:00
xcfs Updated libssh2/openssl to dynamic versions 2021-05-28 08:22:14 +02:00
.gitignore Example of FMake usage 2021-01-14 21:00:43 +03:00
.gitmodules Commit before merge: wasm3 2021-08-09 18:38:09 +02:00
.travis.yml Updating Xcode project to use binary frameworks (XC 12+) 2020-07-06 21:21:51 +02:00
CODE_OF_CONDUCT.md Create CODE_OF_CONDUCT.md 2018-11-05 21:05:51 +01:00
LICENSE Create LICENSE 2018-11-05 21:07:07 +01:00
Package.swift Updated checksums for v3.0.2 2022-11-09 18:00:49 +01:00
README.md Edited instructions 2021-06-19 21:13:35 +02:00
createXcFrameworks.sh Enable arm64 for simulator and enable bitcode for all frameworks 2021-01-18 11:39:41 +03:00
getopt.c "ls" uses the thread_std* 2018-01-18 14:39:43 +01:00
getopt_long.c Working update with pipes, ssh+command, bug fix for getopt_long 2018-01-23 21:06:48 +01:00
ios_error.h Avoid an issue when pthread_self() is reset before the end of 2023-02-04 19:47:45 +01:00
ios_system.m Allows for scripts beginning with a webAssembly command 2023-04-14 22:06:33 +02:00
libc_replacement.c Changes for piping webassembly commands into each other 2023-04-14 22:07:20 +02:00
ncurses_dll.h First commit 2017-12-05 14:44:34 +01:00
ssh_main.c Improvements for iOS_system: duplicate environment at each "fork", 2021-05-25 23:09:30 +02:00
termcap.h Removed references to Python2, uname gives more useful information on 2020-09-07 13:14:09 +02:00

README.md

ios_system: Drop-in replacement for system() in iOS programs

Platform: iOS Build Status Twitter

When porting Unix utilities to iOS (vim, TeX, python...), sometimes the source code executes system commands, using system() calls. These calls are rejected at compile time, with: error: 'system' is unavailable: not available on iOS.

This project provides a drop-in replacement for system(). Simply add the following lines at the beginning of you header file:

extern int ios_system(char* cmd);
#define system ios_system

link with the ios_system.framework, and your calls to system() will be handled by this framework.

Commands available: shell commands (ls, cp, rm...), archive commands (curl, scp, sftp, tar, gzip, compress...) plus a few interpreted languages (python, lua, TeX). Scripts written in one of the interpreted languages are also executed, if they are in the $PATH.

The commands available are defined in two dictionaries, Resources/commandDictionary.plist and Resources/extraCommandsDictionary.plist. At startup time, ios_system loads these dictionaries and enables the commands defined inside. You will need to add these two dictionaries to the "Copy Bundle Resources" step in your Xcode project.

Each command is defined inside a framework. The framework is loaded when the command is called, and released after the command exits. Frameworks for small commands are in this project. Frameworks for interpreted languages are larger, and available separately: python, lua and TeX.

Network-based commands (nslookup, dig, host, ping, telnet) are also available as a separate framework, network_ios. Place the compiled library with the other libraries and add it to the embedded libraries of your application.

This ios_system framework has been successfully integrated into four shells, Blink, OpenTerm, Pisth and LibTerm, an editor, iVim and a TeX-writing app, TeXable. Each time, it provides a Unix look-and-feel (well, mostly feel).

Issues: In iOS, you cannot write in the ~ directory, only in ~/Documents/, ~/Library/ and ~/tmp. Most Unix programs assume the configuration files are in $HOME. So either you redefine $HOME to ~/Documents/ or you set configuration variables (using setenv) to some other place. This is done in the initializeEnvironment() function.

Here's what I have:

setenv PATH = $PATH:~/Library/bin:~/Documents/bin
setenv PYTHONHOME = $HOME/Library/
setenv SSH_HOME = $HOME/Documents/
setenv CURL_HOME = $HOME/Documents/
setenv HGRCPATH = $HOME/Documents/.hgrc/
setenv SSL_CERT_FILE = $HOME/Documents/cacert.pem

Your Mileage May Vary. Note that iOS already defines $HOME and $PATH.

Installation:

The easy way: (Xcode 12 and above) ios_system is available as a set of binary frameworks. Add this project as "Swift Package dependency", and link and embed the frameworks as you need them.

The semi-hard way:

Type swift run --package-path xcfs build. This will download all the requirements (libssh2 and openssl) and build all the ios_system XcFrameworks, in the .build directory.

The hard way:

  • Open the Xcode project ios_system.xcodeproj and hit build. This will create the ios_system framework, ready to be included in your own projects.
  • Compile the other targets as well: files, tar, curl, awk, shell, text, ssh_cmd. This will create the corresponding frameworks.
  • Alternatively, type xcodebuild -project ios_system.xcodeproj -alltargets -sdk iphoneos -configuration Release -quiet to build all the frameworks.
  • If you need python, lua, TeX or network_ios, download the corresponding projects and compile them. All these projects need the ios_system framework to compile.

Integration with your app:

  • Link your application with the ios_system.framework framework.
  • Embed (but don't link) the frameworks corresponding to the commands you need (libtar.dylib if you need tar, libfiles.dylib for cp, rm, mv...).
  • Add the two dictionaries, Resources/commandDictionary.plist and Resources/extraCommandsDictionary.plist to the "Copy Bundle Resources" step in your Xcode project.

Basic commands:

The simplest way to integrate ios_system into your app is to just replace all calls to system() with calls to ios_system(). If you need more control and information, the following functions are available:

  • initializeEnvironment() sets environment variables to sensible defaults.
  • ios_executable(char* inputCmd) returns true if inputCmd is one of the commands defined inside ios_system.
  • NSArray* commandsAsArray() returns an array with all the commands available, if you need them for helping users.
  • NSString* commandsAsString() same, but with a NSString*.
  • NSString* getoptString(NSString* command) returns a string containing all accepted flags for a given command ("dfiPRrvW" for "rm", for example). Letters are followed by ":" if the flag cannot be combined with others.
  • NSString* operatesOn(NSString* command) tells you what this command expects as arguments, so you can auto-complete accordingly. Return values are "file", "directory" or "no". For example, "cd" returns "directory".
  • int ios_setMiniRoot(NSString* mRoot) lets you set the sandbox directory, so users are not exposed to files outside the sandbox. The argument is the path to a directory. It will not be possible to cd to directories above this one. Returns 1 if succesful, 0 if not.
  • FILE* ios_popen(const char* inputCmd, const char* type) opens a pipe between the current command and inputCmd. (drop-in replacement for popen).

More advance control:

replaceCommand: replaceCommand(NSString* commandName, int (*newFunction)(int argc, char *argv[]), bool allOccurences) lets you replace an existing command implementation with your own, or add new commands without editing the source.

Sample use: replaceCommand(@"ls", gnu_ls_main, true);: Replaces all calls to ls to calls to gnu_ls_main. The last argument tells whether you want to replace only the function associated with ls (if false) or all the commands that used the function previously associated with ls(if true). For example, compress and uncompress are both done with the same function, compress_main (and the actual behaviour depends on argv[0]). Only you can know whether your replacement function handles both roles, or only one of them.

If the command does not already exist, your command is simply added to the list.

addCommandList: NSError* addCommandList(NSString* fileLocation) loads several commands at once, and adds them to the list of existing commands. fileLocation points to a plist file, with the same syntax as Resources/extraCommandsDictionary.plist: the key is the command name, and is followed by an Array of 4 Strings: name of the framework, name of the function to call, list of options (in getopt() format) and what the command expects as argument (file, directory, nothing). The last two can be used for autocomplete. The name of the framework can be MAIN if your command is defined in your main program (equivalent to the RTLD_MAIN_ONLY option for dlsym()), or SELF if it is defined inside ios_system.framework (equivalent to RTLD_SELF).

Example:

<key>rlogin</key>
  <array>
    <string>network_ios.framework/network_ios</string>
    <string>rlogin_main</string>
    <string>468EKLNS:X:acde:fFk:l:n:rs:uxy</string>
    <string>no</string>
  </array>

ios_execv(const char path, char const argv[]): executes the command in argv[0] with the arguments argv (it doesn't use path). It is not a drop-in replacement for execv because it does not terminate the current process. execv is usually called after fork(), and execv terminates the child process. This is not possible in iOS. If dup2 was called before execv to set stdin and stdout, ios_execv tries to do the right thing and pass these streams to the process started by execv.

ios_execve also exists, and stores the environment.

Adding more commands:

ios_system is OpenSource; you can extend it in any way you want. Keep in mind the intrinsic limitations:

  • Sandbox and API limitations still apply. Commands that require root privilege (like traceroute) are impossible.
  • Inside terminals we have limited interaction. Apps that require user input are unlikely to get it, or with no visual feedback. That could be solved, but it is hard.

To add a command:

  • (Optional) create an issue: https://github.com/holzschu/ios_system/issues That will let others know you're working on it, and possibly join forces with you (that's the beauty of OpenSource).
  • find the source code for the command, preferrably with BSD license. Apple OpenSource is a good place to start. Compile it first for OSX, to see if it works, and go through configuration.
  • make the following changes to the code:
    • change the main() function into command_main().
    • include ios_error.h.
    • link with ios_system.framework; this will replace most function calls by ios_system version (exit, warn, err, errx, warnx, printf, write...)
    • replace calls to isatty() with calls to ios_isatty().
    • usually, this is enough for your command to compile, and sometimes to run. Check that it works.
    • if you have no output: find where the output happens. Within ios_system, standard output must go to thread_stout. libc_replacement.c intercepts most of the output functions, but not all.
    • if you have issues with input: find where it happens. Standard input comes from thread_stdin.
    • make sure you initialize all variables at startup, and release all memory on exit.
    • make all global variables thread-local with __thread, make sure local variables are marked with static.
    • make sure your code doesn't use commands that don't work in a sandbox: fork, exec, system, popen, isExecutableFileAtPath, access... (some of these fail at compile time, others fail silently at run time).
    • compile the digital library, add it to the embedded frameworks of your app.
    • Edit the Resources/extraCommandsDictionary.plist to add your command, and run.
    • That's it.
    • Test a lot. Side effects can appear after several launches.

Frequently asked commands: here is a list of commands that are often requested, and my experience with them:

  • ping, nslookup, telnet: now provided in the network_ios package.
  • traceroute and most network analysis tools: require root privilege, so impossible inside a sandbox.
  • unzip: use tar -xz.
  • sh, bash, zsh: shells are hard to compile, even without the sandbox/API limitations. They also tend to take a lot of memory, which is a limited asset.
  • git: WorkingCopy does it very well, and you can transfer directories to your app, then transfer back to WorkingCopy. Also difficult to compile.

Licensing:

ios_system itself is released under the Revised BSD License (3-clause BSD license). Foe the other tools, I've used the BSD version as often as possible:

Using BSD versions has consequences on the flags and how they work. For example, there are two versions of sed, the BSD version and the GNU version. They have roughly the same behaviour, but differ on -i (in place): the GNU version overwrites the file if you don't provide an extension, the BSD version won't work unless you provide the extension to use on the backup file (and will backup the input file with that extension).