static void send_error_packet(int to_peer, int err, const char* msg) { size_t msg_size = strlen(msg); if (msg_size > UINT16_MAX) msg_size = UINT16_MAX; struct xfer_msg m = { .type = XFER_MSG_ERROR, .u.error.err = err, .u.error.msg_size = msg_size, }; send_xfer_msg(to_peer, &m); write_all(to_peer, msg, msg_size); } int xfer_stub_main(const struct cmd_xfer_stub_info* info) { struct xfer_ctx ctx = { .from_peer = STDIN_FILENO, .to_peer = STDOUT_FILENO, .info = info, }; struct errinfo ei = { .want_msg = true }; if (catch_error(do_xfer, &ctx, &ei)) { send_error_packet(ctx.to_peer, ei.err, ei.msg); return 1; } return 0; } #if FBADB_MAIN int xfer_handle_command( const struct start_peer_info* spi, const struct cmd_xfer_stub_info* local, const struct cmd_xfer_stub_info* remote) { struct child* peer = start_peer( spi, make_args_cmd_xfer_stub( CMD_ARG_NAME | CMD_ARG_FORWARDED, remote)); struct xfer_ctx ctx = { .from_peer = peer->fd[1]->fd, .to_peer = peer->fd[0]->fd, .info = local, }; do_xfer(&ctx); return child_status_to_exit_code(child_wait(peer)); }
/* * While inside interactive mode, launch the external command cmd on the given * file. */ void wins_launch_external (char *file, char *cmd) { char *arg[] = { cmd, file, NULL }; int pid; wins_prepare_external (); if ((pid = fork_exec (NULL, NULL, cmd, arg))) child_wait (NULL, NULL, pid); wins_unprepare_external (); }
static void main_loop(void) { struct pollfd pollv[1]; int i; int e = 0; char *c; pollv[0].fd = my_sigpipe[0]; pollv[0].events = POLLIN; for(;;){ if(flag_term){ break; } /* start/restart children: */ /* deux[0] is logger, check it first: */ for(i = 0; i < 2; ++i){ if(deux[i].pid == 0){ child_exec(i); } } /* poll on sigpipe: */ sigset_unblock(&my_sigset); do{ e = poll(pollv, 1, -1); }while((e == -1) && (errno == EINTR)); sigset_block(&my_sigset); /* consume sigpipe: */ while(read(my_sigpipe[0], &c, 1) == 1){/*empty*/ ;} /* consume dead children: */ child_wait(); } /* here on SIGTERM: */ if(deux[1].pid){ /* harvest deux[1]: */ waitpid(deux[1].pid, NULL, 0); } if(deux[0].pid){ /* logger should exit on eof: */ close(my_logpipe[1]); do_kill(deux[0].pid, SIGCONT); waitpid(deux[0].pid, NULL, 0); } return; }
static void compile_dex_with_dexopt(const char* dex_file_name, const char* odex_file_name) { SCOPED_RESLIST(rl); int dex_file = xopen(dex_file_name, O_RDONLY, 0); const char* odex_temp_filename = xaprintf( "%s.tmp.%s", odex_file_name, gen_hex_random(ENOUGH_ENTROPY)); cleanup_commit(cleanup_allocate(), cleanup_tmpfile, odex_temp_filename); int odex_temp_file = xopen(odex_temp_filename, O_RDWR | O_CREAT | O_EXCL, 0644); allow_inherit(dex_file); allow_inherit(odex_temp_file); struct child_start_info csi = { .io[0] = CHILD_IO_DEV_NULL, .io[1] = CHILD_IO_PIPE, .io[2] = CHILD_IO_DUP_TO_STDOUT, .exename = "dexopt", .argv = ARGV( "dexopt", "--zip", xaprintf("%d", dex_file), xaprintf("%d", odex_temp_file), dex_file_name, "v=ao=fm=y"), }; struct child* dexopt = child_start(&csi); struct growable_buffer output = slurp_fd_buf(dexopt->fd[1]->fd); int status = child_status_to_exit_code(child_wait(dexopt)); if (status != 0) die(EINVAL, "dexopt failed: %s", massage_output_buf(output)); xrename(odex_temp_filename, odex_file_name); } static void compile_dex(const char* dex_file_name, const char* odex_file_name) { if (api_level() < 21) compile_dex_with_dexopt(dex_file_name, odex_file_name); } int rdex_main(const struct cmd_rdex_info* info) { const char* dex_file_name = info->dexfile; struct stat dex_stat = xstat(dex_file_name); const char* odex_file_name = make_odex_name(dex_file_name); bool need_recompile = true; struct stat odex_stat; if (stat(odex_file_name, &odex_stat) == 0 && dex_stat.st_mtime <= odex_stat.st_mtime) { need_recompile = false; } (void) need_recompile; (void) odex_file_name; (void) compile_dex; if (need_recompile) compile_dex(dex_file_name, odex_file_name); if (setenv("CLASSPATH", dex_file_name, 1) == -1) die_errno("setenv"); if (info->classname[0] == '-') die(EINVAL, "class name cannot begin with '-'"); execvp("app_process", (char* const*) ARGV_CONCAT( ARGV("app_process", xdirname(dex_file_name), info->classname), info->args ?: empty_argv)); die_errno("execvp(\"app_process\", ..."); }
static struct child* start_child(struct msg_shex_hello* shex_hello) { if (shex_hello->nr_argv < 2) die(ECOMM, "insufficient arguments given"); SCOPED_RESLIST(rl_args); char** child_args = read_child_arglist(shex_hello->nr_argv); reslist_pop_nodestroy(rl_args); struct child_start_info csi = { .flags = CHILD_SETSID, .exename = child_args[0], .argv = (const char* const *) child_args + 1, .pty_setup = setup_pty, .pty_setup_data = shex_hello, .deathsig = -SIGHUP, }; if (shex_hello->si[0].pty_p) csi.flags |= CHILD_PTY_STDIN; if (shex_hello->si[1].pty_p) csi.flags |= CHILD_PTY_STDOUT; if (shex_hello->si[2].pty_p) csi.flags |= CHILD_PTY_STDERR; return child_start(&csi); } static void __attribute__((noreturn)) re_exec_as_root() { execlp("su", "su", "-c", xaprintf("%s stub", orig_argv0), NULL); die_errno("execlp of su"); } int stub_main(int argc, const char** argv) { if (argc != 1) die(EINVAL, "this command is internal"); /* XMKRAW_SKIP_CLEANUP so we never change from raw back to cooked * mode on exit. The connection dies on exit anyway, and * resetting the pty can send some extra bytes that can confuse * our peer. */ if (isatty(0)) xmkraw(0, XMKRAW_SKIP_CLEANUP); if (isatty(1)) xmkraw(1, XMKRAW_SKIP_CLEANUP); printf(FB_ADB_PROTO_START_LINE "\n", build_time, (int) getuid()); fflush(stdout); struct msg_shex_hello* shex_hello; struct msg* mhdr = read_msg(0, read_all_adb_encoded); if (mhdr->type == MSG_EXEC_AS_ROOT) re_exec_as_root(); // Never returns if (mhdr->type != MSG_SHEX_HELLO || mhdr->size < sizeof (struct msg_shex_hello)) { die(ECOMM, "bad hello"); } shex_hello = (struct msg_shex_hello*) mhdr; struct child* child = start_child(shex_hello); struct stub stub; memset(&stub, 0, sizeof (stub)); stub.child = child; struct fb_adb_sh* sh = &stub.sh; sh->process_msg = stub_process_msg; sh->max_outgoing_msg = shex_hello->maxmsg; sh->nrch = 5; struct channel** ch = xalloc(sh->nrch * sizeof (*ch)); ch[FROM_PEER] = channel_new(fdh_dup(0), shex_hello->stub_recv_bufsz, CHANNEL_FROM_FD); ch[FROM_PEER]->window = UINT32_MAX; ch[FROM_PEER]->adb_encoding_hack = true; replace_with_dev_null(0); ch[TO_PEER] = channel_new(fdh_dup(1), shex_hello->stub_send_bufsz, CHANNEL_TO_FD); replace_with_dev_null(1); ch[CHILD_STDIN] = channel_new(child->fd[0], shex_hello->si[0].bufsz, CHANNEL_TO_FD); ch[CHILD_STDIN]->track_bytes_written = true; ch[CHILD_STDIN]->bytes_written = ringbuf_room(ch[CHILD_STDIN]->rb); ch[CHILD_STDOUT] = channel_new(child->fd[1], shex_hello->si[1].bufsz, CHANNEL_FROM_FD); ch[CHILD_STDOUT]->track_window = true; ch[CHILD_STDERR] = channel_new(child->fd[2], shex_hello->si[2].bufsz, CHANNEL_FROM_FD); ch[CHILD_STDERR]->track_window = true; sh->ch = ch; io_loop_init(sh); PUMP_WHILE(sh, (!channel_dead_p(ch[FROM_PEER]) && !channel_dead_p(ch[TO_PEER]) && (!channel_dead_p(ch[CHILD_STDOUT]) || !channel_dead_p(ch[CHILD_STDERR])))); if (channel_dead_p(ch[FROM_PEER]) || channel_dead_p(ch[TO_PEER])) { // // If we lost our peer connection, make sure the child sees // SIGHUP instead of seeing its stdin close: just drain any // internally-buffered IO and exit. When we lose the pty, the // child gets SIGHUP. // // Make sure we won't be getting any more commands. channel_close(ch[FROM_PEER]); channel_close(ch[TO_PEER]); PUMP_WHILE(sh, (!channel_dead_p(ch[FROM_PEER]) || !channel_dead_p(ch[TO_PEER]))); // Drain output buffers channel_close(ch[CHILD_STDIN]); PUMP_WHILE(sh, !channel_dead_p(ch[CHILD_STDIN])); return 128 + SIGHUP; } // // Clean exit: close standard handles and drain IO. Peer still // has no idea that we're exiting. Get exit status, send that to // peer, then cleanly shut down the peer connection. // dbg("clean exit"); channel_close(ch[CHILD_STDIN]); channel_close(ch[CHILD_STDOUT]); channel_close(ch[CHILD_STDERR]); PUMP_WHILE (sh, (!channel_dead_p(ch[CHILD_STDIN]) || !channel_dead_p(ch[CHILD_STDOUT]) || !channel_dead_p(ch[CHILD_STDERR]))); send_exit_message(child_wait(child), sh); channel_close(ch[TO_PEER]); PUMP_WHILE(sh, !channel_dead_p(ch[TO_PEER])); channel_close(ch[FROM_PEER]); PUMP_WHILE(sh, !channel_dead_p(ch[FROM_PEER])); return 0; }
struct child_start_info csi = { .flags = CHILD_MERGE_STDERR, .exename = "adb", .argv = argv_concat((const char*[]){"adb", NULL}, adb_args ?: empty_argv, (const char*[]){"push", local, remote, NULL}, NULL), }; struct child* adb = child_start(&csi); fdh_destroy(adb->fd[0]); char buf[512]; size_t len = read_all(adb->fd[1]->fd, buf, sizeof (buf)); fdh_destroy(adb->fd[1]); int status = child_wait(adb); if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) { if (len == sizeof (buf)) --len; while (len > 0 && isspace(buf[len - 1])) --len; buf[len] = '\0'; char* epos = buf; while (*epos != '\0' && isspace(*epos)) ++epos; if (strncmp(epos, "error: ", strlen("error: ")) == 0) { epos += strlen("error: ");