static size_t channel_wanted_readsz(struct channel* c) { if (c->dir != CHANNEL_FROM_FD) return 0; if (c->fdh == NULL) return 0; return XMIN(ringbuf_room(c->rb), c->window); }
void dbgch(const char* label, struct channel** ch, unsigned nrch) { SCOPED_RESLIST(rl_dbgch); unsigned chno; dbglock(); dbg("DBGCH[%s]", label); for (chno = 0; chno < nrch; ++chno) { struct channel* c = ch[chno]; struct pollfd p = channel_request_poll(ch[chno]); const char* pev; switch (p.events) { case POLLIN | POLLOUT: pev = "POLLIN,POLLOUT"; break; case POLLIN: pev = "POLLIN"; break; case POLLOUT: pev = "POLLOUT"; break; case 0: pev = "NONE"; break; default: pev = xaprintf("%xd", p.events); break; } assert(p.fd == -1 || p.fd == c->fdh->fd); dbg(" %-18s size:%-4zu room:%-4zu window:%-4d %s%-2s %p %s", xaprintf("ch[%d=%s]", chno, chname(chno)), ringbuf_size(c->rb), ringbuf_room(c->rb), c->window, (c->dir == CHANNEL_FROM_FD ? "<" : ">"), ((c->fdh != NULL) ? xaprintf("%d", c->fdh->fd) : (c->sent_eof ? "!!" : "!?")), c, pev); } }
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; }