Giter VIP home page Giter VIP logo

ios_system's Introduction

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.

The commands available are defined in ios_system.m. They are dynamically loaded at run-time, and released after execution. They are configurable by changing the dynamic libraries embedded into the app.

There are, first, 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.

For each set of commands, we need to provide the corresponding dynamic library. Libraries for small commands are in this project. Library or Frameworks for interpreted languages are larger, and available separately: python, lua and TeX. Some commands (curl, python) require OpenSSH and libssl2, which you will have to download and compile separately.

Network-based commands (nslookup, dig, host, ping, telnet) are also available as a separate dynamic library, 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 ported into two shells, Blink and OpenTerm and into an editor, iVim. 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.

scp and sftp:

scpand sftp are implemented by rewriting them as curl commands. For example, scp user@host:~/distantfile localfile becomes (internally) curl scp://user@host/~/distantFile -o localFile. This was done to keep the size of the framework as small as possible. It will work for most of the users and most of the commands. However, it has consequences:

  • scp from distant file to distant file probably won't work, only from local to distant or distant to local.
  • The flags are those from curl, not scp. Except -q (quiet) which I remapped to -s (silent).
  • The config file is .curlrc, not .ssh/config.
  • The library used internally is libssh2, not OpenSSH.

Installation:

The easy way: run the script ./get_binaries.sh. This will download the compiled versions of all existing frameworks (ios_system.framework, plus all the dynamic libraries, including network_ios).

The hard way:

  • Run the script ./get_sources.sh. This will download the latest sources form Apple OpenSource and patch them for compatibility with iOS.
  • We need libSSH2 and OpenSSL. Either:
    • [Fast] Run the script ./get_frameworks.sh to download precompiled versions of openSSL.framework and libSSH2.framework.
    • [Slow] Run the script ./get_frameworks_as_source.sh to download the source for openSSL.framework and libSSH2.framework, compile them, and move them to Frameworks.
  • 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 dynamic libraries.
  • Alternatively, type xcodebuild -project ios_system.xcodeproj -alltargets -sdk iphoneos -configuration Debug -quiet to build all targets.
  • 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, and embed (but don't link) the dynamic libraries corresponding to the commands you need (libtar.dylib if you need tar, libfiles.dylib for cp, rm, mv...).

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.

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, but is just a pointer to ios_execv (we don't do anything with the environment for now).

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:

  • 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:
    • include ios_error.h (it will replace all calls to exit by calls to pthread_exit)
    • replace calls to warn, err, errx and warnx by calls to fprintf, plus pthread_exit if needed.
    • replace all occurences of stdin, stdout, stderr by thread_stdin, thread_stdout, thread_stderr (these are thread-local variables, taking a different value for each thread so we can pipe commands).
    • replace all calls to printf, write,... with explicit calls to fprintf(thread_stdout, ...) (ios_error.h takes care of some of these).
    • replace STDIN_FILENO with fileno(stdin). Replace STDOUT_FILENO by calls to fprintf or fwrite; fileno(thread_stdout) does not always exist (it can be a stream with no files associated). Same with stderr.
    • replace calls to isatty() by tests (stdout == thread_stdout). Normally, this is true if we're in the iOS equivalent of a tty.
    • 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, edit ios_system.m to add your commands, and run. That's it. Test a lot. Side effects appear after several launches.
    • if your command has a large code base, work out the difference in your edits and make a patch, rather than commit the entire code. See get_sources_for_patching.sh for an example.

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.
  • nano, ed: require user interaction, so currently impossible.
  • vim: like ed, but even more difficult (needs to access the entire screen, need to add lines to the keyboard for Escape, Tab... iVim is on the App Store, and can be accessed from inside OpenTerm using the share command. My fork of iVim can launch shell commands with :!. It's easier to make an editor start commands than to make a terminal run an editor.
  • 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:

As much as possible, I used the BSD version of the tools. More precisely:

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).

ios_system's People

Contributors

holzschu avatar ian-mcdowell avatar louisdh avatar n-holzschuch avatar palmin avatar pducks32 avatar yury avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.