mirror of
http://galexander.org/git/simplesshd.git
synced 2025-01-27 23:40:55 +00:00
add 'buffersu' program to wrap rsync and make sure there is no wedging
when SuperSU is combined with rsync
This commit is contained in:
parent
2349aef08c
commit
a916ac5c08
20
NOTES
20
NOTES
@ -176,5 +176,25 @@ SIOCGIFCONF, just like ifconfig does, but it works fine with
|
|||||||
java.net.NetworkInterface.
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
XXX - document rsync buffer and supersu integration
|
||||||
|
XXX - new release
|
||||||
|
|
||||||
XXX - if you remove it from the recent apps list, does it stop the service??
|
XXX - if you remove it from the recent apps list, does it stop the service??
|
||||||
XXX - support password-based logins?
|
XXX - support password-based logins?
|
||||||
|
1
doit
1
doit
@ -2,6 +2,7 @@ ndk-build -j8 &&
|
|||||||
mv libs/armeabi/scp libs/armeabi/libscp.so &&
|
mv libs/armeabi/scp libs/armeabi/libscp.so &&
|
||||||
mv libs/armeabi/sftp-server libs/armeabi/libsftp-server.so &&
|
mv libs/armeabi/sftp-server libs/armeabi/libsftp-server.so &&
|
||||||
mv libs/armeabi/rsync libs/armeabi/librsync.so &&
|
mv libs/armeabi/rsync libs/armeabi/librsync.so &&
|
||||||
|
mv libs/armeabi/buffersu libs/armeabi/libbuffersu.so &&
|
||||||
ant release &&
|
ant release &&
|
||||||
pbinst bin/SimpleSSHD-release.apk
|
pbinst bin/SimpleSSHD-release.apk
|
||||||
# cat bin/SimpleSSHD-release.apk | ssh roach 'cat > /sdcard/buh.apk; source .profile; am start --user 0 -d file:///sdcard/buh.apk'
|
# cat bin/SimpleSSHD-release.apk | ssh roach 'cat > /sdcard/buh.apk; source .profile; am start --user 0 -d file:///sdcard/buh.apk'
|
||||||
|
@ -587,7 +587,11 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
|
|||||||
cmd = t;
|
cmd = t;
|
||||||
} else if (cmd && !strncmp(cmd, "rsync ", 6)) {
|
} else if (cmd && !strncmp(cmd, "rsync ", 6)) {
|
||||||
char *t = malloc(strlen(cmd)+strlen(NDK_EXECUTABLES_PATH)+80);
|
char *t = malloc(strlen(cmd)+strlen(NDK_EXECUTABLES_PATH)+80);
|
||||||
sprintf(t, "%s/lib%s.so %s", NDK_EXECUTABLES_PATH, "rsync",
|
char *x = "rsync";
|
||||||
|
if (conf_rsyncbuffer) {
|
||||||
|
x = "buffersu";
|
||||||
|
}
|
||||||
|
sprintf(t, "%s/lib%s.so %s", NDK_EXECUTABLES_PATH, x,
|
||||||
cmd+6);
|
cmd+6);
|
||||||
cmd = t;
|
cmd = t;
|
||||||
}
|
}
|
||||||
|
@ -597,3 +597,17 @@ LOCAL_LDLIBS :=
|
|||||||
LOCAL_LDFLAGS := -static
|
LOCAL_LDFLAGS := -static
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
include $(BUILD_EXECUTABLE)
|
||||||
|
|
||||||
|
|
||||||
|
# build separate sftp executable
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_CFLAGS :=
|
||||||
|
LOCAL_MODULE := buffersu
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := buffersu.c
|
||||||
|
# LOCAL_LDLIBS :=
|
||||||
|
LOCAL_LDFLAGS := -static
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
||||||
|
217
jni/buffersu.c
Normal file
217
jni/buffersu.c
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#define WRAPPED_CMD "/data/data/org.galexander.sshd/lib/librsync.so"
|
||||||
|
#define WRAPPED_ARG0 "rsync"
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
die(void)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
waitpid(-1, &status, WNOHANG);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile int child_dead = 0;
|
||||||
|
static volatile pid_t child_pid = 0;
|
||||||
|
|
||||||
|
static void
|
||||||
|
sigchld(int sig)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
waitpid(-1, &status, WNOHANG);
|
||||||
|
/* don't exit, in case child has unprocessed output */
|
||||||
|
child_dead = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sigpipe(int sig)
|
||||||
|
{
|
||||||
|
kill(child_pid, SIGPIPE);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BLKSZ 1024
|
||||||
|
struct block {
|
||||||
|
struct block *next;
|
||||||
|
struct block *prev;
|
||||||
|
char buf[BLKSZ];
|
||||||
|
int ofs,len;
|
||||||
|
};
|
||||||
|
struct buf {
|
||||||
|
struct block *head; /* read here */
|
||||||
|
struct block *tail; /* write here */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
buf_waiting(struct buf *b)
|
||||||
|
{
|
||||||
|
return (b->head != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
buf_read(struct buf *b, int fd)
|
||||||
|
{
|
||||||
|
struct block *p;
|
||||||
|
char t[BLKSZ];
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = read(fd, t, BLKSZ);
|
||||||
|
if (n < 0) {
|
||||||
|
perror("read");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
if (n == 0) {
|
||||||
|
/* EOF */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
p = malloc(sizeof *p);
|
||||||
|
memcpy(p->buf, t, BLKSZ);
|
||||||
|
p->next = NULL;
|
||||||
|
p->prev = b->tail;
|
||||||
|
if (b->tail) {
|
||||||
|
b->tail->next = p;
|
||||||
|
}
|
||||||
|
p->ofs = 0;
|
||||||
|
p->len = n;
|
||||||
|
b->tail = p;
|
||||||
|
if (!b->head) {
|
||||||
|
b->head = p;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
buf_write(struct buf *b, int fd)
|
||||||
|
{
|
||||||
|
struct block *p;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (!b->head) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p = b->head;
|
||||||
|
|
||||||
|
n = write(fd, p->buf+p->ofs, p->len);
|
||||||
|
if (n <= 0) {
|
||||||
|
perror("write");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
p->ofs += n;
|
||||||
|
p->len -= n;
|
||||||
|
if (p->len <= 0) {
|
||||||
|
if (p->next) {
|
||||||
|
b->head = p->next;
|
||||||
|
p->next->prev = NULL;
|
||||||
|
} else {
|
||||||
|
b->head = NULL;
|
||||||
|
b->tail = NULL;
|
||||||
|
}
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int p0[2], p1[2];
|
||||||
|
pid_t pid;
|
||||||
|
pipe(p0);
|
||||||
|
pipe(p1);
|
||||||
|
if ((pid=fork())) {
|
||||||
|
/* parent */
|
||||||
|
fd_set ifds, ofds;
|
||||||
|
int child_stdin, child_stdout;
|
||||||
|
struct buf buf0 = { 0 };
|
||||||
|
struct buf buf1 = { 0 };
|
||||||
|
int nfd = 2;
|
||||||
|
|
||||||
|
close(p0[0]);
|
||||||
|
close(p1[1]);
|
||||||
|
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("fork");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
child_pid = pid;
|
||||||
|
|
||||||
|
signal(SIGPIPE, sigpipe);
|
||||||
|
signal(SIGCHLD, sigchld);
|
||||||
|
|
||||||
|
child_stdin = p0[1];
|
||||||
|
child_stdout = p1[0];
|
||||||
|
if (child_stdin > nfd) nfd = child_stdin;
|
||||||
|
if (child_stdout > nfd) nfd = child_stdout;
|
||||||
|
nfd++;
|
||||||
|
while (1) {
|
||||||
|
int s;
|
||||||
|
FD_ZERO(&ifds);
|
||||||
|
FD_SET(0, &ifds);
|
||||||
|
FD_SET(child_stdout, &ifds);
|
||||||
|
FD_ZERO(&ofds);
|
||||||
|
if (buf_waiting(&buf1)) {
|
||||||
|
FD_SET(1, &ofds);
|
||||||
|
} else if (child_dead) {
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
if (!child_dead && buf_waiting(&buf0)) {
|
||||||
|
FD_SET(child_stdin, &ofds);
|
||||||
|
}
|
||||||
|
s = select(nfd, &ifds, &ofds, NULL, NULL);
|
||||||
|
if (s < 0) {
|
||||||
|
perror("select");
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
fprintf(stderr, "select %d\n", s);
|
||||||
|
#define T(x) \
|
||||||
|
if (FD_ISSET(0, &x##s)) fprintf(stderr, #x " 0\n"); \
|
||||||
|
if (FD_ISSET(1, &x##s)) fprintf(stderr, #x " 1\n"); \
|
||||||
|
if (FD_ISSET(child_stdin, &x##s)) fprintf(stderr, #x " child_stdin\n"); \
|
||||||
|
if (FD_ISSET(child_stdout, &x##s)) fprintf(stderr, #x " child_stdout\n");
|
||||||
|
T(ifd)
|
||||||
|
T(ofd)
|
||||||
|
#endif
|
||||||
|
if (FD_ISSET(0, &ifds)) {
|
||||||
|
buf_read(&buf0, 0);
|
||||||
|
}
|
||||||
|
if (FD_ISSET(child_stdout, &ifds)) {
|
||||||
|
if (!buf_read(&buf1, child_stdout)) {
|
||||||
|
child_dead = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (FD_ISSET(1, &ofds)) {
|
||||||
|
buf_write(&buf1, 1);
|
||||||
|
}
|
||||||
|
if (!child_dead && FD_ISSET(child_stdin, &ofds)) {
|
||||||
|
buf_write(&buf0, child_stdin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
die();
|
||||||
|
} else {
|
||||||
|
/* child */
|
||||||
|
char **child_argv;
|
||||||
|
close(0);
|
||||||
|
close(1);
|
||||||
|
dup2(p0[0], 0);
|
||||||
|
dup2(p1[1], 1);
|
||||||
|
close(p0[0]);
|
||||||
|
close(p0[1]);
|
||||||
|
close(p1[0]);
|
||||||
|
close(p1[1]);
|
||||||
|
child_argv = malloc((argc+1) * sizeof *child_argv);
|
||||||
|
memcpy(child_argv, argv, argc*sizeof *child_argv);
|
||||||
|
child_argv[0] = WRAPPED_ARG0;
|
||||||
|
child_argv[argc] = NULL;
|
||||||
|
execv(WRAPPED_CMD, child_argv);
|
||||||
|
perror("execv");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
@ -38,6 +38,7 @@ extern const char *conf_path;
|
|||||||
extern const char *conf_shell;
|
extern const char *conf_shell;
|
||||||
extern const char *conf_home;
|
extern const char *conf_home;
|
||||||
const char *conf_path_file(const char *fn);
|
const char *conf_path_file(const char *fn);
|
||||||
|
extern int conf_rsyncbuffer;
|
||||||
|
|
||||||
|
|
||||||
#endif /* __CONFIG_H__ */
|
#endif /* __CONFIG_H__ */
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
const char *conf_path = "", *conf_shell = "", *conf_home = "";
|
const char *conf_path = "", *conf_shell = "", *conf_home = "";
|
||||||
|
int conf_rsyncbuffer = 0;
|
||||||
|
|
||||||
/* NB - this will leak memory like crazy if called often.... */
|
/* NB - this will leak memory like crazy if called often.... */
|
||||||
const char *
|
const char *
|
||||||
@ -117,7 +118,8 @@ from_java_string(jobject s)
|
|||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_galexander_sshd_SimpleSSHDService_start_1sshd(JNIEnv *env_,
|
Java_org_galexander_sshd_SimpleSSHDService_start_1sshd(JNIEnv *env_,
|
||||||
jclass cl,
|
jclass cl,
|
||||||
jint port, jobject jpath, jobject jshell, jobject jhome, jobject jextra)
|
jint port, jobject jpath, jobject jshell, jobject jhome, jobject jextra,
|
||||||
|
jint rsyncbuffer)
|
||||||
{
|
{
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
const char *extra;
|
const char *extra;
|
||||||
@ -129,6 +131,7 @@ Java_org_galexander_sshd_SimpleSSHDService_start_1sshd(JNIEnv *env_,
|
|||||||
conf_shell = from_java_string(jshell);
|
conf_shell = from_java_string(jshell);
|
||||||
conf_home = from_java_string(jhome);
|
conf_home = from_java_string(jhome);
|
||||||
extra = from_java_string(jextra);
|
extra = from_java_string(jextra);
|
||||||
|
conf_rsyncbuffer = rsyncbuffer;
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
android:title="Login Shell"
|
android:title="Login Shell"
|
||||||
android:summary="Location of sh binary"
|
android:summary="Location of sh binary"
|
||||||
android:defaultValue="/system/bin/sh" />
|
android:defaultValue="/system/bin/sh" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="rsyncbuffer"
|
||||||
|
android:title="Buffer hack for rsync+SuperSU"
|
||||||
|
android:defaultValue="false" />
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:key="home"
|
android:key="home"
|
||||||
android:title="Home Directory"
|
android:title="Home Directory"
|
||||||
|
@ -16,6 +16,9 @@ public class Prefs {
|
|||||||
public static boolean get_onboot() {
|
public static boolean get_onboot() {
|
||||||
return pref.getBoolean("onboot", false);
|
return pref.getBoolean("onboot", false);
|
||||||
}
|
}
|
||||||
|
public static boolean get_rsyncbuffer() {
|
||||||
|
return pref.getBoolean("rsyncbuffer", false);
|
||||||
|
}
|
||||||
public static int get_port() {
|
public static int get_port() {
|
||||||
int ret;
|
int ret;
|
||||||
try {
|
try {
|
||||||
|
@ -65,7 +65,8 @@ public class SimpleSSHDService extends Service {
|
|||||||
}
|
}
|
||||||
start_sshd(Prefs.get_port(),
|
start_sshd(Prefs.get_port(),
|
||||||
Prefs.get_path(), Prefs.get_shell(),
|
Prefs.get_path(), Prefs.get_shell(),
|
||||||
Prefs.get_home(), Prefs.get_extra());
|
Prefs.get_home(), Prefs.get_extra(),
|
||||||
|
(Prefs.get_rsyncbuffer() ? 1 : 0));
|
||||||
|
|
||||||
if (sshd_pid != 0) {
|
if (sshd_pid != 0) {
|
||||||
final int pid = sshd_pid;
|
final int pid = sshd_pid;
|
||||||
@ -99,7 +100,8 @@ public class SimpleSSHDService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static native void start_sshd(int port, String path,
|
private static native void start_sshd(int port, String path,
|
||||||
String shell, String home, String extra);
|
String shell, String home, String extra,
|
||||||
|
int rsyncbuffer);
|
||||||
private static native void stop_sshd();
|
private static native void stop_sshd();
|
||||||
private static native int waitpid(int pid);
|
private static native int waitpid(int pid);
|
||||||
static {
|
static {
|
||||||
|
Loading…
Reference in New Issue
Block a user