From 77fd2d635c1c139d6f0d44fa8f5db97e468e43ab Mon Sep 17 00:00:00 2001 From: Alexey 'Cluster' Avdyukhin Date: Wed, 1 Mar 2017 15:14:54 +0300 Subject: Stable --- clovershell.c | 349 +++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 297 insertions(+), 52 deletions(-) diff --git a/clovershell.c b/clovershell.c index 2c786a9..02d1a0e 100644 --- a/clovershell.c +++ b/clovershell.c @@ -9,9 +9,11 @@ #include #include #include +#include #include #include -#include +#include +#include #define DEVICE_PATH "/dev/usb_clover" char* cmd = "/bin/su"; @@ -20,6 +22,10 @@ char* arg1 = "root"; char* arg2 = "-c"; #define LOGIN_PROG "getty 0 -L %s vt102" #define MAX_SHELL_CONNECTIONS 256 +#define MAX_EXEC_CONNECTIONS 256 +#define WRITE_BUFFER_SIZE 32768 +#define READ_BUFFER_SIZE 32768 +#define CLEANUP_INTERVAL 60 #define CMD_PING 0 #define CMD_PONG 1 @@ -42,13 +48,24 @@ char* arg2 = "-c"; struct shell_connection { - int pid; + int reading_pid; int shell_pid; int fdm; char fds[128]; }; +struct exec_connection +{ + int stdout_pid; + int stderr_pid; + int exec_pid; + int stdin[2]; + int stdout[2]; + int stderr[2]; +}; + struct shell_connection* shell_connections[MAX_SHELL_CONNECTIONS]; +struct exec_connection* exec_connections[MAX_EXEC_CONNECTIONS]; int u = 0; @@ -64,43 +81,21 @@ void sig_handler(int signo) exit(0); } -int new_shell_connection() +void shell_read_thread(struct shell_connection* c, int id) { - int i; - for(i = 0; i < MAX_SHELL_CONNECTIONS; i++) - if (!shell_connections[i]) - { - shell_connections[i] = malloc(sizeof(struct shell_connection)); - memset(shell_connections[i], 0, sizeof(struct shell_connection)); - return i; - } - printf("too many shell connections\n"); - return -1; -} - -void shell_thread(struct shell_connection* c, int id) -{ - char buff[1024*10]; - - buff[0] = CMD_SHELL_NEW_RESP; - buff[1] = id; - buff[2] = buff[3] = buff[4] = buff[5] = 0; - *((long int*)&buff[2]) = sizeof(int); - *((int*)&buff[6]) = c->pid; - write(u, buff, 6+sizeof(int)); + char buff[WRITE_BUFFER_SIZE]; // reading pseudo-tty in a loop while (1) { - long int l = read(c->fdm, buff+6, sizeof(buff-6)); + long int l = read(c->fdm, buff+4, sizeof(buff)-4); if (l > 0) { // send received data to USB, including CMD and ID buff[0] = CMD_SHELL_OUT; buff[1] = id; - buff[2] = buff[3] = buff[4] = buff[5] = 0; - *((long int*)&buff[2]) = l; - if (write(u, buff, l+6) < 0) + *((uint16_t*)&buff[2]) = l; + if (write(u, buff, l+4) < 0) { printf("usb write error\n"); exit(0); @@ -109,8 +104,9 @@ void shell_thread(struct shell_connection* c, int id) printf("tty %d(%s) eof\n", id, c->fds); buff[0] = CMD_SHELL_CLOSED; buff[1] = id; - buff[2] = buff[3] = buff[4] = buff[5] = 0; - write(u, buff, 6); + buff[2] = buff[3] = 0; + write(u, buff, 4); + close(u); exit(0); } } @@ -120,8 +116,8 @@ void shell_new_connection() { int id; for(id = 0; id < MAX_SHELL_CONNECTIONS; id++) - if (!shell_connections[id]) - break; + if (!shell_connections[id]) + break; if (id >= MAX_SHELL_CONNECTIONS) { printf("too many shell connections\n"); @@ -130,24 +126,22 @@ void shell_new_connection() shell_connections[id] = malloc(sizeof(struct shell_connection)); memset(shell_connections[id], 0, sizeof(struct shell_connection)); struct shell_connection* c = shell_connections[id]; - c->fdm = posix_openpt(O_RDWR|O_NOCTTY); + c->fdm = posix_openpt(O_RDWR|O_NOCTTY); if (c->fdm < 0) error("posix_openpt"); if (grantpt(c->fdm)) error("grantpt"); if (unlockpt(c->fdm)) error("unlockpt"); ptsname_r(c->fdm, c->fds, sizeof(c->fds)); printf("created %d(%s)\n", id, c->fds); - // reading thread - if (!(c->pid = fork())) - { - shell_thread(c, id); - return; - } + char buff[4]; + buff[0] = CMD_SHELL_NEW_RESP; + buff[1] = id; + buff[2] = buff[3] = 0; + write(u, buff, 4); // starting getty if (!(c->shell_pid = fork())) { - sleep(1); close(u); close(c->fdm); char g[128]; @@ -155,9 +149,13 @@ void shell_new_connection() execl(cmd, arg0, arg1, arg2, g, NULL); error("exec getty"); } + + // reading thread + if (!(c->reading_pid = fork())) + shell_read_thread(c, id); } -void shell_data(int id, char* data, uint32_t len) +void shell_data(int id, char* data, uint16_t len) { struct shell_connection* c = shell_connections[id]; if (!c) @@ -169,7 +167,7 @@ void shell_data(int id, char* data, uint32_t len) { printf("fdm %d(%s) write error\n", id, c->fds); close(c->fdm); - kill(c->pid, SIGTERM); + kill(c->reading_pid, SIGKILL); free(shell_connections[id]); shell_connections[id] = NULL; } @@ -180,8 +178,8 @@ void shell_kill(int id) struct shell_connection* c = shell_connections[id]; if (!c) return; close(c->fdm); - kill(c->shell_pid, SIGTERM); - kill(c->pid, SIGTERM); + kill(c->shell_pid, SIGKILL); + kill(c->reading_pid, SIGKILL); free(shell_connections[id]); shell_connections[id] = NULL; printf("shell session %d killed\n", id); @@ -194,18 +192,249 @@ void shell_kill_all() if (shell_connections[id]) shell_kill(id); } -int main() +void exec_read_stdout_thread(struct exec_connection* c, int id) +{ + char buff[WRITE_BUFFER_SIZE]; + + // reading pipe in a loop + while (1) + { + int l = read(c->stdout[0], buff+4, sizeof(buff)-4); + buff[0] = CMD_EXEC_STDOUT; + buff[1] = id; + if (l > 0) + { + *((uint16_t*)&buff[2]) = l; + if (write(u, buff, l+4) < 0) + exit(1); + } else { + buff[2] = buff[3] = 0; + if (write(u, buff, 4) < 0) + exit(1); + close(u); + exit(0); + } + } +} + +void exec_read_stderr_thread(struct exec_connection* c, int id) +{ + char buff[WRITE_BUFFER_SIZE]; + + // reading pipe in a loop + while (1) + { + int l = read(c->stderr[0], buff+4, sizeof(buff)-4); + buff[0] = CMD_EXEC_STDERR; + buff[1] = id; + if (l > 0) + { + *((uint16_t*)&buff[2]) = l; + if (write(u, buff, l+4) < 0) + exit(1); + } else { + buff[2] = buff[3] = 0; + if (write(u, buff, 4) < 0) + exit(1); + close(u); + exit(0); + } + } +} + +void exec_new_connection(char* cmd, uint16_t len) +{ + cmd[len] = 0; + int id; + for(id = 0; id < MAX_EXEC_CONNECTIONS; id++) + if (!exec_connections[id]) + break; + if (id >= MAX_EXEC_CONNECTIONS) + { + printf("too many shell connections\n"); + return; + } + exec_connections[id] = malloc(sizeof(struct exec_connection)); + memset(exec_connections[id], 0, sizeof(struct exec_connection)); + struct exec_connection* c = exec_connections[id]; + + printf("executing %s\n", cmd); + + char buff[1024]; + buff[0] = CMD_EXEC_NEW_RESP; + buff[1] = id; + *((uint16_t*)&buff[2]) = len; + strcpy(&buff[4], cmd); + write(u, buff, 4+len); + + pipe(c->stdin); + pipe(c->stdout); + pipe(c->stderr); + + // executing + if (!(c->exec_pid = fork())) + { + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + dup2(c->stdin[0], STDIN_FILENO); + close(c->stdin[0]); + close(c->stdin[1]); // unused + dup2(c->stdout[1], STDOUT_FILENO); + close(c->stdout[1]); + dup2(c->stderr[1], STDERR_FILENO); + close(c->stderr[1]); + //execl("/bin/sh", "sh", "-c", cmd, (char *) 0); + int ret = system(cmd); + buff[0] = CMD_EXEC_RESULT; + buff[1] = id; + *((uint16_t*)&buff[2]) = sizeof(ret); + *((int*)&buff[4]) = ret; + write(u, buff, 4+sizeof(ret)); + close(u); + exit(0); + } + + close(c->stdin[0]); // unused in this thread + + // stdout reading thread + if (!(c->stdout_pid = fork())) + { + close(c->stdin[1]); // unused + close(c->stdout[1]); // unused + close(c->stderr[0]); // unused + close(c->stderr[1]); // unused + exec_read_stdout_thread(c, id); + } + // stderr reading thread + if (!(c->stderr_pid = fork())) + { + close(c->stdin[1]); // unused + close(c->stderr[1]); // unused + close(c->stdout[0]); // unused + close(c->stdout[1]); // unused + exec_read_stderr_thread(c, id); + } + + // unused + close(c->stdout[0]); + close(c->stdout[1]); + close(c->stderr[0]); + close(c->stderr[1]); +} + +void exec_stdin(int id, char* data, uint16_t len) +{ + struct exec_connection* c = exec_connections[id]; + if (!c) + { + printf("invalid id: %d\n", id); + return; + } + if (len > 0) + { + if (write(c->stdin[1], data, len) < 0) + printf("exec %d write error\n", id); + } else close(c->stdin[1]); +} + +void exec_kill(int id) +{ + struct exec_connection* c = exec_connections[id]; + if (!c) return; + close(c->stdin[1]); + kill(c->exec_pid, SIGKILL); + kill(c->stdout_pid, SIGKILL); + kill(c->stderr_pid, SIGKILL); + free(exec_connections[id]); + exec_connections[id] = NULL; + printf("exec session %d killed\n", id); +} + +void exec_kill_all() +{ + int id; + for (id = 0; id < MAX_EXEC_CONNECTIONS; id++) + if (exec_connections[id]) exec_kill(id); +} + +void cleanup() +{ + // Zombie hunt! + int id; + for (id = 0; id < MAX_SHELL_CONNECTIONS; id++) + { + struct shell_connection* c = shell_connections[id]; + if (c) + { + //printf("Checking shell %d\n", id); + char dead = 1; + if (c->reading_pid && (waitpid(c->reading_pid, NULL, WNOHANG) == 0)) + dead = 0; + else + c->reading_pid = 0; + if (c->shell_pid && (waitpid(c->shell_pid, NULL, WNOHANG) == 0)) + dead = 0; + else + c->shell_pid = 0; + //printf("Dead: %d\n", dead); + if (dead) + { + close(c->fdm); + free(c); + shell_connections[id] = NULL; + } + } + } + for (id = 0; id < MAX_EXEC_CONNECTIONS; id++) + { + struct exec_connection* c = exec_connections[id]; + if (c) + { + //printf("Checking exec %d\n", id); + char dead = 1; + if (c->exec_pid && (waitpid(c->exec_pid, NULL, WNOHANG) == 0)) + dead = 0; + else + c->exec_pid = 0; + if (c->stdout_pid && (waitpid(c->stdout_pid, NULL, WNOHANG) == 0)) + dead = 0; + else + c->stdout_pid = 0; + if (c->stderr_pid && (waitpid(c->stderr_pid, NULL, WNOHANG) == 0)) + dead = 0; + else + c->stderr_pid = 0; + //printf("Dead: %d\n", dead); + if (dead) + { + close(c->stdin[1]); + free(c); + exec_connections[id] = NULL; + } + } + } + +} + +int main(int argc, char **argv) { printf("clovershell (c) cluster, 2017 (built time: %s %s)\n", __DATE__, __TIME__); int i; for(i = 0; i < MAX_SHELL_CONNECTIONS; i++) shell_connections[i] = NULL; + for(i = 0; i < MAX_EXEC_CONNECTIONS; i++) + exec_connections[i] = NULL; if (signal(SIGTERM, sig_handler) == SIG_ERR) error("SIGTERM"); u = open(DEVICE_PATH, O_RDWR|O_NOCTTY, 0); if (u < 0) error("usb open"); - char buff[65536]; + char buff[READ_BUFFER_SIZE]; + time_t clean_time = time(NULL); + + if (argc >= 2 && strcmp(argv[1], "--daemon") == 0) + daemon(1,1); while (1) { @@ -218,14 +447,12 @@ int main() } char cmd = buff[0]; char arg = buff[1]; - uint32_t len = *((long int*)&buff[2]); + uint16_t len = *((uint16_t*)&buff[2]); //printf("cmd=%d, arg=%d, len=%d\n", cmd, arg, len); - char* data = &buff[6]; - if (len + 6 != l) + char* data = &buff[4]; + if (len + 4 != l) { printf("invalid size: %d != %d\n", l, len); - //close(u); - //exit(0); continue; } @@ -248,6 +475,24 @@ int main() case CMD_SHELL_KILL_ALL: shell_kill_all(); break; + case CMD_EXEC_NEW_REQ: + exec_new_connection(data, len); + break; + case CMD_EXEC_STDIN: + exec_stdin(arg, data, len); + break; + case CMD_EXEC_KILL: + exec_kill(arg); + break; + case CMD_EXEC_KILL_ALL: + exec_kill_all(); + break; + } + if (time(NULL) - clean_time > CLEANUP_INTERVAL) + { + //printf("Cleaing up\n"); + cleanup(); + clean_time = time(NULL); } } return 0; -- cgit v1.2.3