December 6, 2014. The idea is to make a proper ssh implementation for Android. Important features: * it should run happily without root (on a non-root port) * it should be a regular android app requiring no special permissions, and not requiring any 'magic' executable files * should not rely on busybox * preferably support sftp * open source The existing apps are either expensive, don't work, need root, or too complicated, or a mix of all of the above. And none of them are open source. I figure I'll start with dropbear, which I will run through JNI instead of putting it in its own binary (because making such a binary executable is a bit of a hack). So that's the plan........ December 14, 2014. I got dropbear to compile under the Android NDK, so now it's time to work on the Android side of it. I need: * a Service that can be started, stopped, and queried for whether it's running or not * a Thread to implement the Service's work (by calling into dropbear's main()), which can also be stopped. * a config UI with at least these choices: - bool: start on boot (def: false) - number: port number (def: 2222) - string: path to authorized_keys file (def: /sdcard/ssh) - string: name of default shell (def: /system/bin/sh -l) - string: default path for HOME (def: /sdcard/ssh) - button: start or (if it's running) stop December 15, 2014. Getting to the fun part. Process management... To start sshd, it seems like I can startService(). Then in the Service's onStartCommand(), call startForeground() so it won't be killed (return START_STICKY too?). The question is if dropbear's main() should run under a separate Thread, or a separate Process. The trouble with a Thread is that it might be hard to kill. The trouble with a process is that there is no way to report back status (such as a failure to start sshd). Connectbot starts a new process for its shell -- it really doesn't have a choice because the shell binary isn't linked with Connectbot, and exec() in a thread stinks. To stop it, it just closes stdin/stdout!!! So zombies can (and do) linger. I suppose dropbear could be in its own process if it had something like stdin/stdout to communicate failure? Or it could just write error messages to (i.e.) /sdcard/ssh/log. To stop the service, it would just use kill(). I am curious how the main waiting-for-connections loop looks, but even if it uses select(), I'm not sure how I would honor Thread.interrupt() or whatever. It's not guaranteed to interrupt select(), and I'm not keen on adding an arbitrary timeout/polling feature to it. December 20, 2014. So, I added a builtin scp endpoint. It was pretty straight forward, except dropbear defaults to vfork(), which blocks the parent until the child runs execve()!! Anyways, I noticed that scp doesn't quote its arguments to the remote scp. That means you can't conveniently copy a remote file with a space in its name (it becomes two files). But the upside is that this is where wildcards are handled -- by the shell! So I need to either run it as a separate executable launched through the shell, or make my own implementation of wildcards. It is easy, using a $(BUILD_EXECUTABLE) script, to get ndk to build an executable. But it is only packaged up if it is named "gdbserver" (and debug apk), or "libfoo.so". The good news is that libfoo.so can be executed in /data/data/org.galexander.sshd/lib/libfoo.so, so that is a viable option. Doing the expansion myself is not necessarily hard either, though. I need a library function called glob(), which is apparently not part of bionic. But I have the idea some cut and paste would resolve that with very little extra work on my part. December 21, 2014. Well, bionic libc *does* provide fnmatch(), and even scandir() (a shortcut for readdir). In the best case, though, that still leaves me with a bit of a path parsing conundrum (I have to tell scandir which directory to operate on). And also a bit of an escape character conundrum -- \* and "*" should not act like wildcards. Those are not insurmountable but I think I've talked myself out of it. So then the question is, do I figure out how to ship an executable, or do I do some hack like open a pipe to "/system/bin/sh echo filespec" and use the shell solely for expansion? I'm developing the idea that it's actually pretty easy to ship an executable, I just need to find some -pre-package step where I can do "mv scp libscp.so" and then it will ship. ndk-build will not let me make a target with a "." in it directly. XXX - sftp (?) XXX - rsync XXX - document (ga.org/software/simplesshd/) XXX - upload