mirror of
http://galexander.org/git/simplesshd.git
synced 2024-11-27 09:48:08 +00:00
1304 lines
57 KiB
Plaintext
1304 lines
57 KiB
Plaintext
BUILD INSTRUCTIONS:
|
|
|
|
|
|
To build this without my private keys, you'll need to replace my
|
|
ant.properties symlink with an empty file. You'll have to build a
|
|
"debug" apk instead of a "release" one. Android will not let you install
|
|
a debug apk on top of a release one, so you have to remove stock
|
|
SimpleSSHD first before installing the debug build.
|
|
|
|
Then follow these steps (roughly the "doit" script):
|
|
|
|
ndk-build -j8 &&
|
|
mv libs/armeabi/scp libs/armeabi/libscp.so &&
|
|
mv libs/armeabi/sftp-server libs/armeabi/libsftp-server.so &&
|
|
mv libs/armeabi/rsync libs/armeabi/librsync.so &&
|
|
mv libs/armeabi/buffersu libs/armeabi/libbuffersu.so &&
|
|
mv libs/x86/scp libs/x86/libscp.so &&
|
|
mv libs/x86/sftp-server libs/x86/libsftp-server.so &&
|
|
mv libs/x86/rsync libs/x86/librsync.so &&
|
|
mv libs/x86/buffersu libs/x86/libbuffersu.so &&
|
|
ant debug
|
|
|
|
The mv steps are very important, because ant will only package the
|
|
necessary binaries if they have a .so extension (even though they are
|
|
stand alone executables).
|
|
|
|
|
|
|
|
DEVELOPMENT JOURNAL:
|
|
|
|
|
|
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.
|
|
|
|
...
|
|
|
|
Now scp, sftp-server, and rsync work as separate executables... rsync
|
|
does fail at -z because it needs it's own custom zlib...The stock
|
|
"external" one seems to lack the "old-style compress" method. There is
|
|
another commandline option for the new (deflate?) technique, but I think
|
|
the normal -z ought to work too. But that is literally the last feature!
|
|
Then just release details.
|
|
|
|
|
|
December 29, 2014.
|
|
|
|
First problem report from a user. Lollipop (Android 5.0) requires "PIE"
|
|
executables -- position independent code. I think that is a modern
|
|
equivalent to -fpic that Android is now requiring so that it can
|
|
randomize addresses to try to obscure stack smashing attacks that rely on
|
|
fixed addresses. It is epicly lame.
|
|
|
|
Anyways, the big fuck-you from Google is that Ice Cream Sandwich (Android
|
|
4.1) and earlier require fixed-position code. So one binary will not
|
|
generally work on both.
|
|
|
|
Here is a good summary:
|
|
https://code.google.com/p/android-developer-preview/issues/detail?id=888
|
|
|
|
There is something called "run_pie" which you can wrap your executables
|
|
in that lets older Android run PIE executables. It would require a
|
|
relatively small change to the exec() call to prepend it with "run_pie".
|
|
That seems like a hack.
|
|
|
|
The suggested remedy is to build two different apks! Yuck!
|
|
|
|
Anyways, it is only executables (not libraries -- they are position
|
|
independent already) that are affected. And apparently static
|
|
executables don't care one way or the other.
|
|
|
|
So that is my remedy -- static executables for the moment. I tested them
|
|
and it is only a little bit bigger -- 904kB of binaries instead of 668kB.
|
|
|
|
|
|
January 18, 2015.
|
|
|
|
Markus Ethen suggested it display the current IP address so you know
|
|
where to ssh to, in case it isn't convenient to use static dhcp or to
|
|
remember the address. That seems to be easier said than done. You can
|
|
use WifiManager, but that won't give your IP address unless you're on
|
|
wifi. That is probably "good enough", but it is certainly not ideal.
|
|
There is also java.net.NetworkInterface, which seems to return a random
|
|
ipv6 address.
|
|
|
|
Ah-hah! It is fe80::macaddr, which is a bogus "local-connection only"
|
|
ipv6 address, like 192.168, but automatically-generated without dhcp.
|
|
So if I skip that, it finds the proper ipv4 address!
|
|
|
|
Pfew! I was thinking I'd have to directly use /proc/net/dev and
|
|
SIOCGIFCONF, just like ifconfig does, but it works fine with
|
|
java.net.NetworkInterface.
|
|
|
|
|
|
June 20, 2015.
|
|
|
|
At some points, rsync is only write()ing, and assumes that the other end
|
|
will receive it all. The other end does a little bit of write()ing of
|
|
its own, and then is happy to read() all it wants. So this written stuff
|
|
may sit in a buffer somewhere indefinitely. If that happens, an
|
|
infelicity in the design of SuperSU causes everything to wedge.
|
|
|
|
Of course, this is only if you set the shell to /system/xbin/su as a way
|
|
of having root access for rsync.
|
|
|
|
Anyways, I made a new program, "buffersu", which is just a
|
|
stdin/stdout-buffering wrapper for rsync that is guaranteed to always
|
|
perform any read() that is possible at any time, no matter how many
|
|
write()s are outstanding. That seems to do the trick.
|
|
|
|
|
|
June 21, 2016.
|
|
|
|
Chris Moore reports that rsync and sftp do not like files larger than 2GB.
|
|
|
|
rsync was easy - it just needed an additional #define in rsync/config.h
|
|
to enable its builtin support for using stat64/lseek64/off64_t/etc.
|
|
|
|
Now that I'm investigating sftp, I find this surprising fact about bionic
|
|
(though the glibc man page for stat(2) tried to tell me this) - stat64
|
|
and stat are the same thing! But off64_t and lseek64 are significant.
|
|
That should make converting sftp pretty convenient. Especially since
|
|
sftp already uses "u_int64_t" instead of off_t.
|
|
|
|
p.s. Chris Moore gave me this command to test sftp, which turned out to
|
|
be useful:
|
|
|
|
curl -v --pubkey .ssh/id_rsa.pub -r 2147482624-2147484672 -k sftp://mushroom:2222/sdcard/ssh/buh -o buh-new
|
|
|
|
As for scp, it's not as clear what needs to be done. It doesn't use
|
|
lseek. But it does use off_t a bit, including on an index in a for loop
|
|
that is compared against st_size (which is 64-bit). So I'll just change
|
|
all of the off_t to off64_t and hope for the best.
|
|
|
|
sftp and rsync work! Not gonna bother testing scp on big files...
|
|
|
|
|
|
October 1, 2016.
|
|
|
|
Jared Stafford told me startForeground() improves responsiveness on
|
|
Nougat. There had been a comment suggesting startForeground(), but I
|
|
never got around to trying it because it has worked "well enough". With
|
|
Nougat, though, there is a definite tendency for SimpleSSHD to be
|
|
non-responsive. I'm not sure exactly what its cause is, but the symptom
|
|
I notice most frequently is that the first ssh connection after a while
|
|
will be delayed "a long time" - on the order of 10-30 seconds, or maybe
|
|
indefinitely sometimes. Oddly, a second connection can sometimes get
|
|
through undelayed, even before the first connection does. It is as if
|
|
the fork() of process for the new connection is where the delay is, not
|
|
in the listen() call.
|
|
|
|
That's not overall too surprising, Nougat is a lot harsher about
|
|
background processes as part of a Google push to reduce power consumption
|
|
on idle devices.
|
|
|
|
Another concern is related to a change back in July - sometimes the
|
|
system will kill the sshd process for no good reason (maybe because they
|
|
removed it from the recent apps list). The remedy I settled on was to
|
|
monitor for the sshd process dying from within the regular
|
|
Android-managed process, and restart it. I guess I didn't write down
|
|
where in the documentation I found it, but Android seems expressly
|
|
antagonistic to non-system-managed processes. If they ever get more
|
|
hard-assed about that, this whole idea goes out the window.
|
|
|
|
Anyways, I implemented startForeground(), and I am unhappy that it
|
|
requires a Notification. At API 7 (Android 2.1) those are really
|
|
primitive. For example, the PRIORITY_MIN behavior which will sometimes
|
|
hide the notification is added in API 16 (Android 4.1). So, I've got it
|
|
with this stupid old-style notification, and it really doesn't look good,
|
|
and it is always present. That is not awesome.
|
|
|
|
On the other hand, it's easy to block the notifications, and a few people
|
|
have expressed an interest in a notification.
|
|
|
|
It doesn't seem worth it to me to upgrade to a newer API just for the
|
|
better notifications... On the other hand, Google Play shows that I have
|
|
862 users:
|
|
Android 7+ : 3.13%
|
|
Android 6+ : 37.47%
|
|
Android 5+ : 67.86%
|
|
Android 4.1+: 96.62%
|
|
Android 4.0+: 98.01%
|
|
|
|
The oldest reported version is Android 2.3.
|
|
|
|
So, there are a few people on very old versions, but actually SimpleSSHD
|
|
is used on newer devices than the average app, which is the reverse of my
|
|
typical trend. So a few people would be negatively impacted, but not a
|
|
very large number. I could switch to multi-APK mode so that legacy users
|
|
are just stuck with an unsupported back-version, which is probably what
|
|
they truly want anyways..
|
|
|
|
Anyways, I'm gonna use it with startForeground() and the notifications
|
|
disabled for a while, and if I find it to be an improvement then I'll
|
|
just make it so clicking on the notification goes to the app, update the
|
|
doc, and then publish it.
|
|
|
|
...
|
|
|
|
Looking up doze mode details for improving a different app, I stumbled
|
|
across this:
|
|
|
|
https://www.bignerdranch.com/blog/diving-into-doze-mode-for-developers/
|
|
|
|
One last case which is not mentioned in the Android documentation is
|
|
an app using a foreground service. Any process using a foreground
|
|
service is exempt from Doze Mode effects, which is key for
|
|
long-running applications that you may want to run even if the user
|
|
has put their phone down for a long period of time -- like a music app,
|
|
for example.
|
|
|
|
There is, however, a bug in Android Marshmallow which requires a
|
|
foreground service to be in a separate process from all other activity
|
|
code, documented here. It is planned to be fixed in Nougat.
|
|
|
|
Not exactly confidence-inspiring. And I haven't come across a
|
|
description of how exactly doze mode screws up SimpleSSHD, either.
|
|
|
|
It might be worthwhile to do the work to find out where things actually
|
|
get wedged, it might even be a flaw in Dropbear that makes it so
|
|
unreliable with Nougat.
|
|
|
|
|
|
October 16, 2016.
|
|
|
|
I've been using a foreground service for a while, and it has not caused
|
|
me any troubles. The thing I have been most anxious about is that it
|
|
might disable doze mode entirely...but there has not been any noteworthy
|
|
overnight drain.
|
|
|
|
So, in case someone does have problems with it, I am making it an option
|
|
that defaults to enabled.
|
|
|
|
Looking through email, I haven't seen any, but I have the idea several
|
|
users have asked for a notification icon in the past. And now that that
|
|
is finally implemented, I am curious about other things people have
|
|
requested that I have not been keen on. And Jan Ondrej's requests come
|
|
to mind:
|
|
|
|
o) setting to start the service automatically when the application is
|
|
launched
|
|
o) QUIT button that stops the service and the activity at once
|
|
o) not allow wifi to power down when the activity is open
|
|
|
|
I'm really not crazy about integrating any kind of wakelock. And having
|
|
two buttons still seems silly to me. But with the notification there,
|
|
the idea that someone will micromanage whether the service is running or
|
|
not does not seem so far fetched.
|
|
|
|
So I'll go ahead and add "Start on Open" setting.
|
|
|
|
...
|
|
|
|
Looking at reviews on Google Play Store, I finally found a couple reviews
|
|
in the last couple months who would have enjoyed password login. I'm
|
|
still pretty opposed to it because it seems like the usage model would be
|
|
to enable a default password, login with it to transfer an
|
|
authorized_keys file, and then disable the default password. That is
|
|
fine, and would even be kind of convenient (I might use it), but it seems
|
|
more often than not the default password would be left enabled
|
|
indefinitely, which is not awesome. And I can't imagine more than 1% of
|
|
users ever typing a strong password into their phones.
|
|
|
|
But I've thought of a compromise. What if, if there is no
|
|
authorized_keys file, it accepts passwordless logins but with some sort
|
|
of obnoxious alert dialog sort of thing to interrupt the user? That way,
|
|
you would be able to login once to copy the authorized keys file, and the
|
|
nuissance alert would be no big deal. Then once the authorized keys
|
|
exists, no further action is necessary.
|
|
|
|
It's a pity there is no convenient way to interact between the Android
|
|
GUI thread and the sshd thread.
|
|
|
|
Ah-hah! I've got the least effort idea in my head! Under situations
|
|
that I'm not sure what they should be, it should generate a random
|
|
password and display it on the phone's screen. Probably it should
|
|
generate it iff there is no authorized_keys file at all. Since that
|
|
detection happens for each client connection, then probably all of this
|
|
logic should go in dropbear itself, and the display should come through
|
|
dropbear.err.
|
|
|
|
Then we might even want a UI way to delete authorized_keys, perhaps even
|
|
as a replacement for the current awkward UI.
|
|
|
|
|
|
November 19, 2016.
|
|
|
|
I got a user request to update for security. I looked at dropbear and
|
|
didn't see any relevant security issues, so I'm gonna hold off for a
|
|
while, but it should be on my radar.
|
|
|
|
I also got a user request for writing to external SD card. He gave
|
|
this link:
|
|
|
|
http://stackoverflow.com/questions/33162152/storage-permission-error-in-marshmallow
|
|
|
|
It gives me a really strong deja vu, I think I tried that a few months
|
|
ago when a nearly identical request came in, and it didn't work. I wish
|
|
I had kept notes for that experiment!
|
|
|
|
I think there are two separate issues. I think Android 6+ push you to
|
|
use requestPermission() to explicitly enable the WRITE_EXTERNAL_STORAGE
|
|
permission, but if targetSdkVersion is low enough then you are
|
|
grandfathered in, so we don't care. The second issue is that Android 5+
|
|
require use of a special API to access a removable SD card (which is
|
|
different from /sdcard, which is typically internal storage protected by
|
|
WRITE_EXTERNAL_STORAGE on new phones?). That second issue is what is
|
|
biting people.
|
|
|
|
I don't know any way around that. If Google doesn't back down, then I
|
|
guess the only plausible way around it would be something like an
|
|
LD_PRELOAD that intercepts open/lseek/read/write/close to external SD,
|
|
and replaces them with connection to a local daemon process that is
|
|
running in a typical Android context and is able to use the awful API.
|
|
|
|
Seems like a lot of work and complication. I might go through with it if
|
|
I happened to use external SD myself, but I'm of the personal opinion
|
|
that removable storage is obsolete now that even relatively cheap phones
|
|
like the base Moto G4 come with 16GB... 640kB ought to be enough for
|
|
anybody.
|
|
|
|
I told the most recent guy to try SuperSU. I don't have any idea if that
|
|
will really work, to be honest.
|
|
|
|
|
|
October 28, 2017.
|
|
|
|
At the beginning of October, a user notified me that rsync doesn't work
|
|
on Android 8.0 (Oreo, API 26). In the past week, Google has upgraded my
|
|
Nexus 5x to Oreo and I can confirm it.
|
|
|
|
It seems that SE Linux or something causes certain system calls to
|
|
perform the equivalent of:
|
|
|
|
fprintf(stderr, "Bad system call\n");
|
|
exit(-1);
|
|
|
|
It should return ENOSYS instead, but someone at Google is not cool enough
|
|
to be working with Unix.
|
|
|
|
The first one I discovered like this is sigprocmask(), which we can
|
|
probably do without. The next one is chmod(), which is somewhat more
|
|
useful.
|
|
|
|
The troubling thing is that I can find a file where the commandline chmod
|
|
works, but the same chmod() doesn't work in rsync.
|
|
|
|
I figured maybe my NDK was just too old ("r10d"), but I think I can't
|
|
conveniently upgrade it because Google only distributes the NDK for Linux
|
|
x86-64 now. I was using SDK 7, so I tried SDK 11, 17, 19, and got the
|
|
same result. The NDK I have also has a directory for SDK 21, but it has
|
|
typos in the header files, or they aren't compatible with its version of
|
|
gcc. But anyways, if SDK 19 doesn't work then maybe it's more than just
|
|
linking against a bad version of Bionic.
|
|
|
|
Well, I think I've got it - fchmod() works, but chmod() does not.
|
|
*shrug*
|
|
|
|
|
|
March 4, 2018.
|
|
|
|
Looked in the google play console for crashes, and I find a few. 10
|
|
people have run into UnsatisfiedLinkError, and they seem to all be using
|
|
x86. According to
|
|
|
|
https://stackoverflow.com/questions/32598756/android-ndkhow-to-exclude-x86-device-in-google-player
|
|
|
|
It's not plausible to fix this problem by blacklisting x86 devices!
|
|
Google sucks! And the justification is that there is an ARM emulation
|
|
layer, but obviously it doesn't work and why should I have to debug it??
|
|
|
|
Anyways, someday may want to try making a jni/Application.mk and setting
|
|
APP_ABI in there to support x86 too? Do I really think that will work
|
|
for scp/rsync/etc? Guh.
|
|
|
|
And this bug has only one report... The startActivity() call to open the
|
|
documentation is getting a android.content.ActivityNotFoundException. I
|
|
guess it must mean there is no browser installed or something? I seem to
|
|
be using the normal way to visit an http programatically.. Only one
|
|
report, so I guess I just don't care.
|
|
|
|
|
|
March 24, 2018.
|
|
|
|
Win Bent emailed me to let me know his x86_64 Asus ZenPad is able to run
|
|
SimpleSSHD but is unable to run rsync. So, the ARM-on-x86 emulation
|
|
layer is catching up and now works sometimes, but isn't compatible with
|
|
the hack for scp/sftp/rsync. Win was kind enough to test a build that
|
|
supports just armeabi and x86, and reported it works. So I don't need to
|
|
build support for both x86 and x86_64, apparently. Nice to have a
|
|
concrete answer to the question!
|
|
|
|
|
|
|
|
May 16, 2018.
|
|
|
|
Roland Jaeger let me know busybox's "su" doesn't appreciate being called
|
|
with "-" prefix to argv[0] for a login shell, it can't find the "-su"
|
|
applet. su wants "-" to come in argv[>=1] instead to indicate a login
|
|
shell.
|
|
|
|
I guess it's kind of a hack but I just don't see it causing much
|
|
trouble... If the string "su" is found in the shell's name, it runs
|
|
"su -" instead of "-su". *shrug*. Roland tested it and reports it works
|
|
for him.
|
|
|
|
He also notes that the default /system/bin/sh distributed with his device
|
|
doesn't run .profile, instead it runs whatever is in $ENV. Apparently
|
|
mksh is "the Android shell", and indeed that is what's on my Nexus 5x.
|
|
mksh is supposed to run /etc/mkshrc (well, /system/etc/mkshrc on Android,
|
|
I guess), and then $ENV. But on my device, it loads .profile too. So I
|
|
guess there is some diversity of configurations. *shrug*
|
|
|
|
NB - bash supports the same idea but it calls the variable $BASH_ENV, or
|
|
$ENV if --posix is set. *shrug* And whatever shell comes with
|
|
SimpleBusyBox (ash?) loads .profile by default.
|
|
|
|
Anyways, it seems the obvious way forward is to make a setting for the
|
|
environment...just one name=value per line.
|
|
|
|
...
|
|
|
|
Noticed a couple crashes in the Play store console. One is a repeat of
|
|
this Note 7 that gives ActivityNotFoundException when viewing
|
|
documentation. I decided to catch the exception and display a dialog
|
|
asking the user to contact me, because I'm curious.
|
|
|
|
The other crash is a NullPointerException due to a bug in older Android
|
|
(a 2013 Nexus 7). It isn't my fault and I don't care.
|
|
https://issuetracker.google.com/issues/36972466
|
|
|
|
There is also an ANR, I think it's my very first! Here's the message:
|
|
Input dispatching timed out (Waiting to send non-key event because the
|
|
touched window has not finished processing certain input events that
|
|
were delivered to it over 500.0ms ago. Wait queue length: 25. Wait
|
|
queue head age: 34478.4ms.)
|
|
I can't tell if it's saying it blocked for 500ms or for 34s, but either
|
|
one seems problematic.
|
|
|
|
It's 4 identical-looking failures on one day, so it might be a fluke.
|
|
It's on x86_64, though I don't think that's the problem...
|
|
|
|
There are a ton of threads so it will be a bit of a trick to try to describe:
|
|
main - waiting in epoll_wait() under a bunch of framework
|
|
MessageQueue/Looper stuff.
|
|
Thread-23 - sleeping in UpdaterThread.run() line 26
|
|
Thread-15 - SimpleSSHDService.run()'s call to native waitpid()
|
|
and just a ton of misc threads that don't seem interesting to me
|
|
|
|
So, it seems like the main thread is specifically waiting for something
|
|
to do, I have no idea why it's not responding to user input. I don't
|
|
think the blocked UpdaterThread or SimpleSSHDService thread, because
|
|
those are specifically in other threads because they are designed to
|
|
block. I'm thinking it's a fluke of some sort, maybe a DoS sort of
|
|
scenario on this one device.
|
|
|
|
Anyways, the other thing I found out at the Play store is I got warned
|
|
because I'm supposed to "target a recent SDK", which means API level 26
|
|
(Android 8.0 Oreo). I think this is dumb but apparently it only means
|
|
I have to update android:targetSdkVersion to 26. I can leave
|
|
minSdkVersion at 7, and target=android-7. I hope.
|
|
|
|
I imagine a bit of UI will move around but it should just be a matter of
|
|
shaking it out and then testing it on an older device.
|
|
|
|
Oh, I just remembered the important new Androidism - permissions. In
|
|
particular, I think it is now more complicated than just
|
|
WRITE_EXTERNAL_STORAGE. Ugh?
|
|
|
|
Using my Nexus 5x (Android 8.1), updating to a new one with
|
|
targetSdkVersion 26, everything seems to just work. But the
|
|
blue-on-white for the IP display becomes blue-on-black and it is almost
|
|
unreadable. And also, it is kind of ugly. I think if I can switch it to
|
|
a dark theme or something that will work itself out.
|
|
|
|
But then I uninstalled the app and installed it from scratch with
|
|
targetSdkVersion 26, and that does not work at all. On startup it shows
|
|
the next "null" on every line where it's supposed to show the log. And
|
|
once the service is started, the log is still null, and anything
|
|
attempting to connect says:
|
|
Connection closed by 192.168.1.23
|
|
|
|
So my guess is that it's now more complicated than just
|
|
WRITE_EXTERNAL_STORAGE. Ugh!
|
|
|
|
...
|
|
|
|
A little info from
|
|
|
|
https://medium.com/google-developers/picking-your-compilesdkversion-minsdkversion-targetsdkversion-a098a0341ebd
|
|
|
|
which told me I can put the compile target much ahead of the min target
|
|
and supposedly I'll get warned if I use new features. Anyways, maybe I
|
|
can gate the new permissions model behind a simple if? I'm not sure if
|
|
it will be enough to explicitly ask for the permission, or if the
|
|
permission is accessed differently.
|
|
|
|
It also suggests I look at the "Platform Highlights" in the API Level
|
|
table at:
|
|
|
|
https://developer.android.com/guide/topics/manifest/uses-sdk-element
|
|
|
|
That's quite a few to check out to go from SDK 11 (Honeycomb) to SDK 26
|
|
(Oreo). But I'll probably learn something...
|
|
|
|
Have to use checkSelfPermission() and requestPermission() to get
|
|
"dangerous" permissions (WRITE_EXTERNAL_STORAGE). Luckily, INTERNET and
|
|
RECEIVE_BOOT_COMPLETED are "normal" permissions and just need to be in
|
|
the AndroidManifest.xml, I guess. And I don't need to worry about
|
|
READ_EXTERNAL_STORAGE, so long as I explicitly request
|
|
WRITE_EXTERNAL_STORAGE.
|
|
|
|
|
|
May 19, 2018.
|
|
|
|
Michael Mess wrote to let me know that he can't use authorized_keys file
|
|
because the /sdcard/ssh/authorized_keys file is group-readwritable. I
|
|
know regular OpenSSH on a PC does complain if the authorized_keys file is
|
|
world-writable, so this made sense to me. He says it is a security flaw,
|
|
but really, it is not, because any app that can run code and write to
|
|
/sdcard/ssh already has all of the permissions that SimpleSSHD has
|
|
(except network, but whatever) -- it isn't escalation, just a horizontal
|
|
spreading.
|
|
|
|
He suggested moving the SSH config into app-private, which is something
|
|
I've been toying with anyways.
|
|
|
|
So after some internal debate, I decided to do two changes:
|
|
|
|
* add "Copy app-private path" to the options menu, which brings up a
|
|
dialog showing the app-private path, with an option "Copy" to put it
|
|
in the clipboard.
|
|
* make the default for new installs for "home" and "path" be the
|
|
app-private dir
|
|
|
|
I look forward to seeing if anyone complains about it.
|
|
|
|
|
|
May 20, 2018.
|
|
|
|
I tried updating the build target to a newer version than minSdkVersion,
|
|
and I got a warning, and I looked at the official google documentation,
|
|
and you can't build with anything newer than the minSdkVersion. I mean,
|
|
I knew that, so, what I'm saying is, I feel stupid for reading that
|
|
medium.com link.
|
|
|
|
|
|
June 8, 2018.
|
|
|
|
Request from Joan Marce i Igual for --iconv. That requires iconv.h,
|
|
which isn't part of Bionic. GNU libiconv will do it, but it doesn't look
|
|
particularly easy to build, and it isn't small. Someone on github has
|
|
uploaded their tree for building iconv on Android, which might save me a
|
|
bunch of effort:
|
|
|
|
https://github.com/ironsteel/iconv-android.git
|
|
|
|
Unfortunately it doesn't have much history to it, so I don't know if
|
|
it'll be worth anything or not but it should give me a sense of the build
|
|
task without forcing me to decipher autoconf.
|
|
|
|
|
|
May 25, 2019.
|
|
|
|
To update to the new SDK (26 required now, 28 required starting in
|
|
August), I have to switch to gradle and all that garbage. Every time I
|
|
update anything with the new SDKs, everything breaks, even after I've
|
|
already updated everything to gradle. So basically it is the worst
|
|
possible development environment.
|
|
|
|
Anyways, the NDK now doesn't support anything older than android-16 (JB
|
|
4.1), which is stupid but whatever. Looking on the play store, it looks
|
|
like I have 5 users on these older devices. I hope it simply refuses to
|
|
update for them, and they can continue to use the older version!
|
|
|
|
... I got it to build and install and run, and it "works". The only
|
|
issue that is currently biting me is (on using am to install an apk):
|
|
|
|
InstallStart: Requesting uid 10180 needs to declare permission
|
|
android.permission.REQUEST_INSTALL_PACKAGES
|
|
|
|
Anyways, there's a bunch of stuff remaining to do, and then I will
|
|
release it.
|
|
|
|
|
|
May 26, 2019.
|
|
|
|
I got an AVD (emulated android) running SDK 26 (Oreo 8.0) x86. It uses
|
|
kvm virtualization so it's much faster than the old emulated ARM I used
|
|
to be shackled with.
|
|
|
|
Anyways, right off, it says (in a toast):
|
|
Developer warning for package "org.galexander.sshd"
|
|
Failed to post notification on channel "null"
|
|
See log for more details
|
|
adb logcat says:
|
|
05-26 17:13:49.877 1741 1764 E NotificationService: No Channel found
|
|
for pkg=org.galexander.sshd, channelId=null, id=1, tag=null,
|
|
opPkg=org.galexander.sshd, callingUid=10085, userId=0, incomingUserId=0,
|
|
notificationUid=10085, notification=Notification(channel=null pri=0
|
|
contentView=org.galexander.sshd/0x7f030001 vibrate=null sound=null
|
|
tick defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE)
|
|
|
|
This gives a kind of deja vu -- I think from BluntMP3? I'm just adding
|
|
it to the todo list.
|
|
|
|
To access SimpleSSHD, run
|
|
adb forward tcp:2222 tcp:2222
|
|
ssh -p 2222 localhost
|
|
|
|
And it demonstrates that the sdcard access is forbidden. In fact, I can
|
|
toggle it inside the System Settings -> Applications -> SimpleSSHD ->
|
|
permissions -> Storage, and it takes effect more or less immediately.
|
|
|
|
Android development is such a constant stream of astonishing garbage.
|
|
When it requests the permission, which happens just as I would want it
|
|
to, it then pops up a "System UI has stopped" dialog. But look, it isn't
|
|
my fault:
|
|
|
|
https://stackoverflow.com/questions/49261555/system-ui-has-stopped-emulator-when-requesting-permissions
|
|
|
|
For real. There's a bug in the emulator you fix by "increasing the DPI."
|
|
I had tried some older 854x480 sort of emulated device because I didn't
|
|
want all those pixels, but I'll just use Pixel 2 (the default, and one
|
|
Google surely tests). It has a different skin where the soft buttons are
|
|
part of the skin, which is I bet what the bug is around.
|
|
|
|
So, the permissions request works now. It just asks once, and no matter
|
|
what the answer is, it never asks again. And if it is grandfathered in
|
|
because the previous version of the app used an older SDK, then it will
|
|
never ask and it'll just have the permission. And, at least on an
|
|
emulated Pixel 2, it can access /sdcard, which is "internal storage."
|
|
It does emulate an "SD card", but it can't access that.
|
|
|
|
...
|
|
|
|
Getting a channel onto the notification was a pain in the ass. All this
|
|
gradle stuff is getting increasingly undocumented. Every time they
|
|
change something so they won't ever have to change anything again, they
|
|
then immediately change everything. This leads to a mass proliferation
|
|
of immediately-obsoleted documents. It even damages resources like
|
|
stackexchange because people take their extraordinarily unique context
|
|
for granted when asking and answering questions. So something that
|
|
worked against gradle 3.3.4 will now be broken against gradle 3.3.5 but
|
|
no one will think to mention what version of gradle, or what version of
|
|
the build scripts gradle runs, or what-have-you.
|
|
|
|
Anyways, the upshot is, the way I got it to work is I opened the project
|
|
in Android Studio, and eventually I used Refactor->Migrate to AndroidX,
|
|
which turned out to have all manner of complications but here we go.
|
|
|
|
Speaking of Android Studio, the AVD Manager (Android Virtual Device, I
|
|
guess) is not accessible under Tools, even though it should be. Instead,
|
|
you have to click on the icon which is a portrait phone with a green
|
|
android head on the lower-right corner of it. Upper-right corner.
|
|
And you have to open a project before it will show you that. Jesus.
|
|
|
|
...
|
|
|
|
Testing with pie, I am able to reproduce the problem with start-on-boot.
|
|
Here's some excerpts from logcat:
|
|
|
|
05-26 21:24:26.251 1837 3408 W ActivityManager: Background start not allowed: service Intent { cmp=org.galexander.sshd/.SimpleSSHDService } to org.galexander.sshd/.SimpleSSHDService from pid=3756 uid=10085 pkg=org.galexander.sshd startFg?=false
|
|
--------- beginning of crash
|
|
05-26 21:24:26.251 3756 3756 E AndroidRuntime: FATAL EXCEPTION: main
|
|
05-26 21:24:26.251 3756 3756 E AndroidRuntime: Process: org.galexander.sshd, PID: 3756
|
|
05-26 21:24:26.251 3756 3756 E AndroidRuntime: java.lang.RuntimeException: Unable to start receiver org.galexander.sshd.BootReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=org.galexander.sshd/.SimpleSSHDService }: app is in background uid UidRecord{5aff37a u0a85 RCVR idle change:uncached procs:1 seq(0,0,0)}
|
|
...
|
|
05-26 21:24:26.251 3756 3756 E AndroidRuntime: Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=org.galexander.sshd/.SimpleSSHDService }: app is in background uid UidRecord{5aff37a u0a85 RCVR idle change:uncached procs:1 seq(0,0,0)}
|
|
...
|
|
05-26 21:24:26.251 3756 3756 E AndroidRuntime: at android.content.ContextWrapper.startService(ContextWrapper.java:664)
|
|
05-26 21:24:26.251 3756 3756 E AndroidRuntime: at org.galexander.sshd.BootReceiver.onReceive(BootReceiver.java:11)
|
|
|
|
So it looks like a pretty intentional rule that you can't start a service
|
|
from a boot receiver??
|
|
|
|
And I have the same problem on my Z2 Force (Oreo 8.0) now that I've
|
|
updated the SDK.
|
|
|
|
This guy says it will allow you to startForegroundService(), so that's
|
|
what I'll try...
|
|
|
|
https://stackoverflow.com/questions/49572570/android-start-a-service-on-boot-not-working
|
|
|
|
Which seems to work! So there is a need for some sort of dance in the
|
|
Settings screen to force it to enable foreground services if
|
|
start-on-boot is selected, and then we'll be golden.
|
|
|
|
|
|
June 16, 2019.
|
|
|
|
I finished the dropbear 2019.78 merge and got it all to work again on the
|
|
x86 version 28 (Pie) emulator. It also works on x86 version 26 (Oreo)
|
|
emulator, and on my ARM Oreo phone (Moto Z2 Force). I also tried with an
|
|
emulated ARM version 16 (JB 4.1), and it works, but it generates a lot of
|
|
this spamming the log:
|
|
|
|
Failed lookup: Non-recoverable failure in name resolution.
|
|
|
|
This gives me a kind of deja vu, something to do with library versions. I
|
|
have the idea it's because I built with a newer libc than what is found
|
|
by dynamic linking in older Android. But I cannot link the main dropbear
|
|
thing with -static because it needs to be JNI (which is loaded with
|
|
dlopen()). Apparently.
|
|
|
|
Anyways, I'm not sure there's anything I can do about that other than
|
|
reduce the log noise. I'm not really interested in digging up a magic
|
|
older version of the NDK or anything like that.
|
|
|
|
But I do think the only reason it has -static on the stand-alone
|
|
executables is basically a hack to make them work with the old NDK, so I
|
|
can get rid of that...
|
|
|
|
Testing with emulated x86 Android 21 (KK 5.0)... It seems to work fine.
|
|
It resolve my IP to the string "127.0.0.1", anyways. rsync seems to
|
|
work, and start-on-boot in the background works.
|
|
|
|
Anyways, I made a naive thing to decode an IP address to a numeric
|
|
string, in case getnameinfo() fails. It is working for me in the ARM
|
|
Android 16 now. It generates a reasonable string even though it
|
|
presumably fails to use the library lookup. It starts on boot as a
|
|
background service. And rsync and scp work. Only need to test on my
|
|
Moto X now.
|
|
|
|
...
|
|
|
|
Testing start-on-boot on my Moto Z2 Force, it works, but there is an
|
|
infelicity. If I go into Settings and try to turn off foreground
|
|
service, it won't let me because start-on-boot is selected and I am using
|
|
Oreo. It is supposed to show a toast to let me know this, but the toast
|
|
doesn't appear. That's because I had previously disabled notifications
|
|
so that the foreground service doesn't have a notification.
|
|
|
|
I can make the toast appear by following these steps: re-enable
|
|
notifications (manually in Settings -> Apps & Notifications), then
|
|
re-start the service, then disable the notification (by long-tapping on
|
|
the notification itself). Then toasts work. I think probably it has
|
|
saved the fact that all notifications are disabled, because it only knew
|
|
one class of notification when it was using compatibility with the old
|
|
SDK...but now it knows two classes (a notification and a toast), and it
|
|
records that only one class is disabled. I really have no idea but I
|
|
think it sucks.
|
|
|
|
...
|
|
|
|
Testing with Moto X (ARM, Android 5.1 API 22), and it works. But it has
|
|
this infelicity...when scp or rsync is run:
|
|
|
|
WARNING: linker: /data/app/org.galexander.sshd-2/lib/arm/libscp.so: unused DT entry: type 0x6ffffffe arg 0xb64
|
|
WARNING: linker: /data/app/org.galexander.sshd-2/lib/arm/libscp.so: unused DT entry: type 0x6fffffff arg 0x2
|
|
|
|
These are DT types that are present in the .so which are not supported by
|
|
the kernel. It may be because the kernel is CyanogenMod? The types are
|
|
DT_VERNEED and DT_VERNEEDNUM. They refer to a section .gnu.version_r,
|
|
which I guess holds two 32-byte entries which are unintelligible to me.
|
|
|
|
Supposedly I can use an "ELF cleaner" to strip out these DT_VERNEED
|
|
things. But there's some ambiguity as to whether they're necessary or
|
|
useful. I think I'm gonna leave that be.
|
|
|
|
More productively, I found that it won't startForegroundService() because
|
|
that was added at Oreo!
|
|
|
|
I can't seem to get start-on-boot to work, but from some of the crap in
|
|
logcat, I have the idea CyanogenMod might not be good at broadcasting the
|
|
boot event.
|
|
|
|
Now for the interesting test...I uninstalled SimpleSSHD on the Moto X
|
|
(API 22), and am reinstalling it new. So it won't have grandfathered
|
|
permissions. It works fine! No surprises. It still doesn't start on
|
|
boot *shrug*.
|
|
|
|
Now to try the same uninstall test with Moto Z2 Force (API 26). It works
|
|
fine, and on the first start-up it does ask for permission to access
|
|
files (/sdcard). To my surprise, it saved my Settings and my
|
|
/data/user/0/org.galexander.sshd/files. That's actually kind of bad,
|
|
because the host key would be saved, and there aren't really many
|
|
settings to re-enter in the upside case. Apparently this is new behavior
|
|
since I added targetSdkVersion>=23, so users haven't had their data
|
|
backed up yet and I can change this setting with allowBackup="false" in
|
|
the manifest...
|
|
|
|
allowBackup="false" took immediate effect and had no surprises...
|
|
|
|
|
|
August 4, 2019.
|
|
|
|
I finally got a dump from a user (Hammad), and it's quite distressing.
|
|
The stack trace is roughly:
|
|
backtrace()
|
|
sigsegv_handler()
|
|
/system/bin/app_process64+0x2a90
|
|
__kernel_rt_sigreturn()
|
|
A5xContext::HwAddNop(unsigned int *, unsigned int)
|
|
EsxCmdMgr::IssuePendingIB1s(EsxFlushReason, int, int)
|
|
EsxCmdMgr::Flush(EsxFlushReason)
|
|
EsxContext::Destroy()
|
|
EglContext::DestroyEsxContext()
|
|
EglDisplay::MarkContextListForDestroy()
|
|
EglDisplay::Terminate(int)
|
|
EglDisplayList::Destroy()
|
|
EglDisplay::DestroyStaticListsMutexesAndTlsKeys()
|
|
EsxEntryDestruct()
|
|
/system/vendor/lib64/egl/libGLESv2_adreno.so+0x12780
|
|
[... cut off at 16 ...]
|
|
|
|
So many questions! I think app_process64 must be the actual C main() of
|
|
a process, responsible for branching into all the android system
|
|
libraries? I imagine it's involved because it's somehow intercepted the
|
|
SIGSEGV and re-dispatched it to my handler? I don't see any way we could
|
|
have branched into libGLESv2_adreno from userland, so the SIGSEGV must
|
|
come from the UI thread, I guess? Maybe this SIGSEGV is actually the
|
|
sort of thing we'd get if we tried to call UI code from the non-UI
|
|
thread??
|
|
|
|
It looks like GLES is busy cleaning itself up, and it crashes. Why's it
|
|
crash? Why's it trying to clean itself up?
|
|
|
|
Hammad says there is no problem using sshd...I thought he meant that the
|
|
re-start logic is working for him but his dropbear.err has multiple dumps
|
|
in it! The SIGSEGVs are apparently not killing the daemon.
|
|
|
|
There are no timestamps on the dumps, but it looks like they're
|
|
associated with activity anyways. Each dump happens between "Disconnect
|
|
received" and "sigchld". Some of them have "server select out"
|
|
interleaved into the dump, which I think is the result of Hammad running:
|
|
while true; do ssh phone 'exit'; done
|
|
That is, it appears he starts a new connection the very instant the old
|
|
connection ends. So the new connection comes into the server process
|
|
while the child process is in the act of dying.
|
|
|
|
The thing is, I don't see how it could possibly be getting signals from
|
|
the Java side of things, because it fork()s before setting up the signal
|
|
handling. It's not just running in a different thread, it should be a
|
|
totally separate process. I can test this but I don't think I'm wrong
|
|
about that.
|
|
|
|
So I guess just about the only thing that's really possible is that
|
|
there's an atexit() which survives the fork() because it isn't followed
|
|
up with an execve(). It's not caused by ARM, or even necessarily by
|
|
Android 9...the reason it doesn't show up in the emulator is that the
|
|
libGLES that registers the atexit() is vendor-supplied for specific
|
|
hardware ("Adreno").
|
|
|
|
So I need to figure out how to bypass the atexit() somehow, perhaps by
|
|
calling _exit() directly?
|
|
|
|
|
|
August 12, 2019.
|
|
|
|
Kumaran Santhanam suggests START and STOP broadcast intents, which seems
|
|
A number 1 to me!
|
|
|
|
And a few people have written back to say the atexit bypass worked for
|
|
their SIGSEGV - pfew!
|
|
|
|
|
|
September 21, 2019.
|
|
|
|
First, some user reports.
|
|
|
|
Kumaran says the START/STOP broadcast intents work wwith Tasker on
|
|
Android 7 but not on Android 9. He also reports it works from the shell
|
|
(am broadcast --user 0 -a org.galexander.sshd.START org.galexander.ssh)
|
|
so I'm not sure what the difference is, and I'm not super inclined to
|
|
install Tasker to find out. I wonder if there is some sort of implicit
|
|
distinction between sending a broadcast intent and sending a service
|
|
intent that is negotiated differently at Android 9?
|
|
|
|
Roman reports that it "works" on his Onyx Boox i68ML (eink, I think), but
|
|
it doesn't refresh the log display so he can never see the
|
|
one-time-password. I think I just need to find a way to manually trigger
|
|
a refresh of the eInk but I haven't found anything like that yet...just a
|
|
bunch of people reporting that other apps (KOReader, CoolReader) stopped
|
|
working on onyx boox at some point. I do remember being somewhat
|
|
surprised that the log view worked at all, so maybe there is something
|
|
missing from my use of TextView and we just have been getting lucky...
|
|
|
|
But while I was looking for a way to get the android emulator to emulate
|
|
eink (no dice), I stumbled onto the Android TV emulation! I haven't
|
|
received one of these requests recently but they used to be a common
|
|
occurrence, and I suspect people would really appreciate if SimpleSSHD
|
|
was just available regularly through the app store on TV.
|
|
|
|
With the emulator, I was able to sideload the app with adb, and then
|
|
enable it under Settings -> Security & Permissions and then launch it
|
|
with Settings -> Apps -> SimpleSSHD -> open. Then it works, except
|
|
there's no three-dots menu button, so no way to get to Settings.
|
|
|
|
Anyways, there's a whole laundry list of things to do to get into the app
|
|
store and to get shown in the regular launcher.
|
|
|
|
...
|
|
|
|
Luckily, even though the list of requirements is long, it is largely
|
|
irrelevant or redundant, so I'm just about there.
|
|
|
|
...
|
|
|
|
On Android, they suggest you make a URL Intent and send it to the system
|
|
browser. But on Android TV, you make a WebView, I guess there is no
|
|
stock Chrome. It's easy enough. The disconcerting part is I can't test
|
|
it on my emulated phone or real phone because WebView.loadUrl() just
|
|
defaults to sending to the system Chrome. That's fine, though,
|
|
apparently it's a known thing you can work around, but I really don't
|
|
need to work around it, assuming Android properly defaults to WebView
|
|
instead if Chrome isn't available. *shrug*
|
|
|
|
So I'm tagging the new version.
|
|
|
|
...
|
|
|
|
When I went to upload version 23, I found that there was a crash reported
|
|
on version 22 with Android 9, two users, a Galaxy S9+ and a OnePlus5T.
|
|
The crash looks like:
|
|
|
|
java.lang.RuntimeException:
|
|
at android.app.ActivityThread.performResumeActivity (ActivityThread.java:3992)
|
|
at android.app.ActivityThread.handleResumeActivity (ActivityThread.java:4024)
|
|
at android.app.servertransaction.ResumeActivityItem.execute (ResumeActivityItem.java:51)
|
|
at android.app.servertransaction.TransactionExecutor.executeLifecycleState (TransactionExecutor.java:145)
|
|
at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:70)
|
|
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1926)
|
|
at android.os.Handler.dispatchMessage (Handler.java:106)
|
|
at android.os.Looper.loop (Looper.java:214)
|
|
at android.app.ActivityThread.main (ActivityThread.java:6990)
|
|
at java.lang.reflect.Method.invoke (Native Method)
|
|
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
|
|
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)
|
|
Caused by: java.lang.IllegalStateException:
|
|
at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1666)
|
|
at android.app.ContextImpl.startService (ContextImpl.java:1611)
|
|
at android.content.ContextWrapper.startService (ContextWrapper.java:677)
|
|
at org.galexander.sshd.SimpleSSHD.onResume (SimpleSSHD.java:62)
|
|
at android.app.Instrumentation.callActivityOnResume (Instrumentation.java:1412)
|
|
at android.app.Activity.performResume (Activity.java:7611)
|
|
at android.app.ActivityThread.performResumeActivity (ActivityThread.java:3984)
|
|
|
|
It's the startService() call for Start On Open. So I tested that on my
|
|
phone (Android 8), and on the emulator with Android 9 and 10, and I
|
|
couldn't get it to happen. Either it's upset that it isn't
|
|
startForegroundService(), or maybe it somehow starts the service even
|
|
after it's already been started and complains about that? It seems like
|
|
it must be intermittent or harmless, because one user has triggered it 6
|
|
times on different days over the week.
|
|
|
|
There's also a crash with version 19 on Android 9, and it only shows as
|
|
far as:
|
|
android.app.ActivityThread.performLaunchActivity
|
|
Which seems to me to maybe be the same spot? Anyways, it didn't recur,
|
|
and I'm not gonna be doing new updates to version 19 anyways, so,
|
|
whatever.
|
|
|
|
|
|
September 22, 2019.
|
|
|
|
The crash on startService() in onResume() has already been verified to
|
|
occur also at version 23, so I have to track that down. I found this,
|
|
which is likely to be the underlying problem:
|
|
|
|
https://stackoverflow.com/questions/52013545/android-9-0-not-allowed-to-start-service-app-is-in-background-after-onresume
|
|
|
|
It looks like the user opens the app with start-on-open, it works, and
|
|
then they let the phone fall asleep with SimpleSSHD on top, and some sort
|
|
of shuffle kills the service, and then they wake the phone up, and then
|
|
it tries to resume SimpleSSHD, which tries to re-start the service, which
|
|
fails because the phone hasn't finished waking up yet so even the top
|
|
activity isn't active yet, even though it's already in
|
|
SimpleSSHD.onResume(). Apparently acknowledged as a bug in Android 9.
|
|
|
|
I tried to synthesize this sequence on the emulator with x86 pie, and I
|
|
had no luck. I was able to get the service to die (by broadcasting STOP)
|
|
while the emulator was "asleep", but then it woke up it didn't cause any
|
|
trouble. I suspect it takes a few coincidences to reliably trigger.
|
|
|
|
Anyways, so what I'm doing is I'm having the startService() calls in
|
|
SimpleSSHD go through the SimpleSSHDService.do_startService() wrapper, so
|
|
that they become startForegroundService() calls as appropriate. This may
|
|
solve the problem on its own, if the user happens to have selected
|
|
foreground. But then I can also make this common path smarter...
|
|
|
|
On another note, I stumbled onto something that says Android 9 limits
|
|
implicit broadcasts (no explicit destination), and considers a broadcast
|
|
to be implicit even if it does have an explicit destination if there is
|
|
an "action string." !!! Not sure if that's something I can play with,
|
|
but putting it on the list.
|
|
|
|
|
|
October 7, 2019.
|
|
|
|
Tom Davidson was having some problems with port proxy. I guess he just
|
|
wants client-side -L and -D to work, which they do work, so far as I
|
|
know. But I tested, and -R does not work! My phone seems to have some
|
|
sort of firewall, it hangs instead of saying connection denied. So maybe
|
|
-R isn't bypassing the firewall. Another possibility is that dropbear
|
|
simply doesn't support -R, but I kind of thought it did?
|
|
|
|
|
|
January 10, 2020.
|
|
|
|
I was here to add Fionn Behrens' request for dark mode support,
|
|
especially for the Notification. And I found, it's already on the list.
|
|
There's a lot on the list.
|
|
|
|
|
|
January 29, 2020.
|
|
|
|
There's a cluster of reviews indicating problems with WinSCP:
|
|
|
|
Wojtek El Jan 9, 2020 at 8:52 AM
|
|
1 star
|
|
Worked great until latest update. Keeps loosing contection when
|
|
uploading file using sftp in WinSCP. Please do something about it then
|
|
I`ll change my rate. Android 5.1
|
|
|
|
Sounds like the new receive window size breaks WinSCP??
|
|
|
|
|
|
March 20, 2020.
|
|
|
|
I happened to install wine today, so I decided to try WinSCP. The newest
|
|
version of WinSCP (5.17.2) doesn't work, it gets an "Invalid access to
|
|
memory" error when connecting. Don't know why, maybe it's wine or maybe
|
|
it's WinSCP. Version 5.16.7 (slightly older) gets the same error.
|
|
Version 4.0.4 is way too old, and can't connect (doesn't know any modern
|
|
ciphers). Version 5.9.3 is just right, I guess. It "works". But then,
|
|
using SFTP to upload a file around 1MB, it reproduces the reported error.
|
|
WinSCP reports "Server sent command status 11". It has a log facility,
|
|
but it shows precious little information. It seems to send about 512kB
|
|
and then it says:
|
|
|
|
. 2020-03-20 20:35:08.191 There are 197377 bytes remaining in the send buffer, need to send at least another 164609 bytes
|
|
. 2020-03-20 20:35:08.191 Looking for network events
|
|
. 2020-03-20 20:35:08.191 Detected network event
|
|
. 2020-03-20 20:35:08.191 Enumerating network events for socket 536
|
|
. 2020-03-20 20:35:08.191 Enumerated 1 network events making 1 cumulative events for socket 536
|
|
. 2020-03-20 20:35:08.191 Handling network read event on socket 536 with error 0
|
|
. 2020-03-20 20:35:08.191 Server sent command exit status 11
|
|
. 2020-03-20 20:35:08.192 Selecting events 0 for socket 536
|
|
. 2020-03-20 20:35:08.192 Disconnected: All channels closed
|
|
. 2020-03-20 20:35:08.192 Connection was lost, asking what to do.
|
|
. 2020-03-20 20:35:08.192 Asking user:
|
|
. 2020-03-20 20:35:08.192 Connection has been unexpectedly closed. Server sent command exit status 11. ()
|
|
|
|
Not sure there's any information there. The SimpleSSHD log shows just a
|
|
normal "Exited normally". Does that mean sftp did an "exit(11)" ? Was
|
|
there a SIGSEGV? Did WinSCP just eat itself for no reason? I have not
|
|
been very impressed with WinSCP (and there is already a user report that
|
|
switching to FileZilla is the right remedy all around), but I would like
|
|
to investigate before throwing in the towel.
|
|
|
|
The file is created on the target with length 65536.
|
|
|
|
Anyways, first thing to do is dick around with the setting that probably
|
|
caused the problem. This is the change:
|
|
|
|
#define DEFAULT_RECV_WINDOW 524288
|
|
#define RECV_MAX_PAYLOAD_LEN (524288+16384)
|
|
|
|
The previous values were apparently
|
|
|
|
#define DEFAULT_RECV_WINDOW 24576
|
|
#define RECV_MAX_PAYLOAD_LEN 32768
|
|
|
|
Combinations DEFAULT_RECV_WINDOW / RECV_MAX_PAYLOAD_LEN.
|
|
512 kB / 512+128 kB broken
|
|
24 kB / 512+128 kB broken
|
|
24 kB / 32 kB fixed
|
|
512 kB / 32 kB fixed
|
|
512-32 kB / 512 kB broken
|
|
512-32 kB / 32 kB fixed
|
|
512-32 kB / 64 kB fixed
|
|
512-32 kB / 128 kB fixed
|
|
512-32 kB / 256 kB fixed
|
|
512-32 kB / 384 kB broken
|
|
|
|
It seems that WinSCP doesn't like RECV_MAX_PAYLOAD_LEN larger than 256kB.
|
|
|
|
Domagoj, who first pointed this out to me, sent this link:
|
|
|
|
https://lab.nexedi.com/nexedi/slapos/commit/605e564b8f3008cdb48dbc6e82c956029469ff02
|
|
|
|
There they say that the -W option allows a major improvement in speed,
|
|
but in order to be compatible with OpenSSH the RECV_MAX_PAYLOAD_LEN must
|
|
also be increased. I only made RECV_MAX_PAYLOAD_LEN "somewhat" bigger
|
|
than DEFAULT_RECV_WINDOW because that was how it was before...
|
|
|
|
So I guess I need to stop poking around in the dark, and find out these
|
|
things really do.
|
|
|
|
DEFAULT_RECV_WINDOW becomes opts.recv_window, which newchannel() uses to
|
|
pick the size of writebuf, and to set chan->recvwindow.
|
|
|
|
RECV_MAX_PAYLOAD_LEN-9 becomes RECV_MAX_CHANNEL_DATA_LEN, then goes into
|
|
chan->recvmaxpacket.
|
|
|
|
Both values are sent across by send_msg_channel_open_init()
|
|
(SSH_MSG_CHANNEL_OPEN), and also by send_msg_channel_open_confirmation()
|
|
(SSH_MSG_CHANNEL_OPEN_CONFIRMATION).
|
|
|
|
It looks like chan->recvwindow is decremented as things come into the
|
|
buffer, and incremented as they leave it. recvmaxpacket doesn't seem to
|
|
do anything other than get sent across.
|
|
|
|
RFC4254 SSH Connection Protocol defines these SSH_MSG_CHANNEL_OPEN
|
|
packets. DEFAULT_RECV_WINDOW is called 'initial window size' and
|
|
"specifies how many bytes of channel data can be sent without adjusting
|
|
the window". RECV_MAX_PAYLOAD_LEN is called 'maximum packet size' and
|
|
"specifies the maximum size of an individual data packet that can be sent
|
|
to the sender."
|
|
|
|
So, really, I think that there is no reason for RECV_MAX_PAYLOAD_LEN to
|
|
be bigger than DEFAULT_RECV_WINDOW. I think DEFAULT_RECV_WINDOW was just
|
|
unreasonably small in the Dropbear defaults, and RECV_MAX_PAYLOAD_LEN
|
|
happened to be the smallest that was permitted by compatibility.
|
|
|
|
A bigger DEFAULT_RECV_WINDOW would allow greater saturation of the pipe
|
|
without worrying about the latency of ACKs. A bigger
|
|
RECV_MAX_PAYLOAD_LEN just saves the overhead of having more packet
|
|
headers, I think. So I think in principle, DEFAULT_RECV_WINDOW will have
|
|
the bigger effect on effective bandwidth, so long as RECV_MAX_PAYLOAD_LEN
|
|
is at least "decently large."
|
|
|
|
So now I'm trying combinations to see performance and compatibility
|
|
OpenSSH (sending a 16.4MB file across ssh 'cat > /dev/null', wall time):
|
|
|
|
512-32 kB / 384 kB works 3.1s 3.4s 3.1s
|
|
512 kB / 32 kB works 2.4s 2.1s 2.0s
|
|
1024 kB / 32 kB works 2.2s 2.0s 2.2s
|
|
24 kB / 32 kB works 10.4s 10.3s 8.9s
|
|
512 kB / 256 kB works 2.3s 2.3s 2.1s
|
|
512 kB / 128 kB works 2.9s 2.1s 2.1s
|
|
512 kB / 512 kB works 2.4s 2.1s 2.2s
|
|
256 kB / 256 kB works 3.4s 4.5s 2.4s 4.7s 2.0s 2.1s
|
|
128 kB / 128 kB works 2.5s 2.3s 2.3s
|
|
64 kB / 64 kB works 3.8s 3.1s 3.2s
|
|
32 kB / 256 kB works 8.9s 9.1s 8.0s
|
|
64 kB / 256 kB works 3.9s 5.6s 3.1s
|
|
512 kB / 128 kB(again) works 2.3s 1.9s 2.0s
|
|
|
|
Obviously a lot of variation from noise on the wireless network, though I
|
|
think the larger receive window actually helps erase some of that
|
|
variability because it doesn't have to wait for the ACK whether the ACK
|
|
is delayed by congestion or not.
|
|
|
|
So, rather frustratingly, I've (somewhat) reproduced the performance
|
|
motivation for changing DEFAULT_RECV_WINDOW, but I haven't found the
|
|
compatibility motivation for changing RECV_MAX_PAYLOAD_LEN. So I'm still
|
|
stabbing in the dark. I suspect that the compatibility problem was
|
|
actually a since-fixed bug in OpenSSH, since ideally it should honor the
|
|
packet length limit regardless of the window size? Or! The report is
|
|
actually that a packet of length 32777 was sent... That's 32768+9, maybe
|
|
the -9 in the definition of RECV_MAX_CHANNEL_DATA_LEN is relatively
|
|
modern in Dropbear?? I bet that's it! It was a phantom all along that
|
|
was forcing RECV_MAX_PAYLOAD_LEN to grow.
|
|
|
|
Anyways, it looks like DEFAULT_RECV_WINDOW really just needs to be bigger
|
|
than 128kB, and RECV_MAX_PAYLOAD_LEN hardly matters at all. So I'm going
|
|
with 512kB / 128kB. And I confirm, that does work with WinSCP.
|
|
|
|
|
|
XXX - ed25519? new dropbear 2020.79!
|
|
|
|
XXX - ability to dynamically request <uses-permission-sdk-23 android:name="android.permission.SEND_SMS" />
|
|
XXX - Vitalii suggests giving an error message for unrecognized key types (ed25519) that are encountered in authorized_keys, so the user doesn't have to stab in the dark
|
|
XXX - on android 6 (duckling moto g2), the notification is white-on-white?
|
|
XXX - test Settings and Notifications colors in Pie (or Quiche?) "dark mode" for Alexander Chobot, and for Fionn Behrens
|
|
XXX - crash.20200109
|
|
XXX - if the unlink(authorized_keys) fails, or if the open() fails for permission reasons, generate a Toast for the user. (confirmed that restorecon -F authorized_keys works)
|
|
XXX - restart the daemon on app upgrade
|
|
<receiver android:name="com.google.android.apps.youtube.app.PackageReplacedReceiver">
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
|
|
</intent-filter>
|
|
</receiver>
|
|
XXX - why doesn't "ssh -R 2223:192.168.1.254:80 mouse" work?
|
|
|
|
XXX - figure out how to force a screen refresh on eink devices (onyx boox) when the password is displayed (Roman)
|
|
XXX - Tasker on Android 9 can't trigger the new START/STOP intents? (Kumaran)
|
|
XXX - test file renaming in /storage/emulated/0/download on Quiche for Ben Reaves
|
|
|
|
--- new release
|
|
|
|
XXX - see if settings looks better with SDK26, if not, hack it so that the ones with strings have their states shown
|
|
|
|
XXX - try /data/data/com.termux/files/usr/bin/zsh as login shell
|
|
|
|
XXX - libiconv? HAVE_ICONV_H etc
|