Пример #1
0
int
pidof_main(const struct cmd_pidof_info* info)
{
    const char* name = info->name;
    DIR* procdir = xopendir("/proc");
    struct dirent* ent;
    unsigned long found_pid = 0;
    bool found = false;
    while ((ent = readdir(procdir)) != NULL) {
        SCOPED_RESLIST(rl);
        char* endptr = NULL;
        errno = 0;
        unsigned long pid = strtoul(ent->d_name, &endptr, 10);
        if (pid == 0 || errno != 0 || *endptr != '\0')
            continue;
        int cmdline_fd = try_xopen(xaprintf("/proc/%lu/cmdline", pid), O_RDONLY, 0);
        if (cmdline_fd == -1)
            continue;
        char* name = slurp_fd(cmdline_fd, NULL);
        if (strcmp(name, info->name) == 0) {
            if (found)
                die(EINVAL, "more than one process has name `%s'", name);
            found = true;
            found_pid = pid;
        }
    }

    if (!found)
        die(ENOENT, "no process has name `%s'", name);
    xprintf(xstdout, "%lu%s", found_pid, info->pidof.zero ? "" : "\n");
    xflush(xstdout);
    return 0;
}
Пример #2
0
static void
dbg_internal(const char* fmt, va_list args)
{
    int saved_errno = errno;
    SCOPED_RESLIST(rl_dbg);
    dbglock();
    fprintf(dbgout, "%s(%4d): ", prgname, getpid());
    vfprintf(dbgout, fmt, args);
    putc('\n', dbgout);
    fflush(dbgout);
    errno = saved_errno;
}
Пример #3
0
void
hack_reopen_tty(int fd)
{
    // We sometimes need O_NONBLOCK on our input and output streams,
    // but O_NONBLOCK applies to the entire file object.  If the file
    // object happens to be a tty we've inherited, everything that
    // uses that tty will start getting EAGAIN and all hell will break
    // loose.  Here, we reopen the tty so we can get a fresh file
    // object and control the blocking mode separately.
    SCOPED_RESLIST(rl_hack);
    xdup3nc(xopen(xttyname(fd), O_RDWR | O_NOCTTY, 0), fd, O_CLOEXEC);
}
Пример #4
0
void
adb_send_file(const char* local,
              const char* remote,
              const char* const* adb_args)
{
    SCOPED_RESLIST(rl_send_stub);

    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},
Пример #5
0
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);
    }
}
Пример #6
0
void
dbg_1(const char* fmt, ...)
{
    int saved_errno = errno;
    if (dbg_enabled_p()) {
        SCOPED_RESLIST(rl_dbg);
        dbglock();
        va_list args;
        fprintf(dbgout, "%s(%04d): ", prgname, getpid());
        va_start(args, fmt);
        vfprintf(dbgout, fmt, args);
        va_end(args);
        fputc('\n', dbgout);
        fflush(dbgout);
    }
    errno = saved_errno;
}
Пример #7
0
static struct property_vector*
find_all_properties_raw(void)
{
    for (;;) {
        SCOPED_RESLIST(rl);

        struct find_all_properties_context ctx = {
            .pv = property_vector_new(),
            .oom = false,
        };

        if (property_foreach(find_all_properties_propfn, &ctx) == 1)
            continue;

        if (ctx.oom)
            die_oom();
        reslist_xfer(rl->parent, rl);
        property_vector_sort(ctx.pv);
        return ctx.pv;
    }
}

static void
property_vector_swap(struct property_vector* a,
                     struct property_vector* b)
{
    struct property_vector tmp = *a;
    *a = *b;
    *b = tmp;
}

static bool
property_vector_equal(const struct property_vector* a,
                      const struct property_vector* b)
{
    return a->size == b->size &&
        memcmp(a->props, b->props, a->size * sizeof (a->props[0])) == 0;
}

static struct property_vector*
find_all_properties(void)
{
    struct property_vector* pv1 = find_all_properties_raw();
    for (;;) {
        SCOPED_RESLIST(pv);
        struct property_vector* pv2 = find_all_properties_raw();
        if (property_vector_equal(pv1, pv2))
            return pv1;
        property_vector_swap(pv1, pv2);
    }
}

static int
compat_property_foreach(
    void (*propfn)(const prop_info *pi, void *cookie),
    void *cookie)
{
    find_symbol_in_libc("__system_property_find_nth",
                        &property_find_nth);
    if (property_find_nth == NULL)
        die(EINVAL, "no property-enumeration functions available");

    unsigned propno = 0;
    for (;;) {
        const prop_info* pi = property_find_nth(propno++);
        if (pi == NULL)
            break;
        propfn(pi, cookie);
    }
    return 0;
}
Пример #8
0
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\", ...");
}
Пример #9
0
static void
set_window_size(int fd, const struct window_size* ws)
{
    int ret;
    struct winsize wz = {
        .ws_row = ws->row,
        .ws_col = ws->col,
        .ws_xpixel = ws->xpixel,
        .ws_ypixel = ws->ypixel,
    };

    do {
        ret = ioctl(fd, TIOCSWINSZ, &wz);
    } while (ret == -1 && errno == EINTR);

    dbg("TIOCSWINSZ(%ux%u): %d", wz.ws_row, wz.ws_col, ret);
}

struct stub {
    struct fb_adb_sh sh;
    struct child* child;
};

static void
stub_process_msg(struct fb_adb_sh* sh, struct msg mhdr)
{
    if (mhdr.type == MSG_WINDOW_SIZE) {
        struct msg_window_size m;
        read_cmdmsg(sh, mhdr, &m, sizeof (m));
        dbgmsg(&m.msg, "recv");
        struct stub* stub = (struct stub*) sh;
        if (stub->child->pty_master)
            set_window_size(stub->child->pty_master->fd, &m.ws);

        return;
    }

    fb_adb_sh_process_msg(sh, mhdr);
}

static void
setup_pty(int master, int slave, void* arg)
{
    struct msg_shex_hello* shex_hello = arg;
    char* hello_end = (char*) shex_hello + shex_hello->msg.size;
    struct termios attr = { 0 };
    xtcgetattr(slave, &attr);
    if (shex_hello->ispeed)
        cfsetispeed(&attr, shex_hello->ispeed);
    if (shex_hello->ospeed)
        cfsetospeed(&attr, shex_hello->ospeed);

    const struct termbit* tb = &termbits[0];
    const struct termbit* tb_end = tb + nr_termbits;
    struct term_control* tc = &shex_hello->tctl[0];

    while ((char*)(tc+1) <= hello_end && tb < tb_end) {
        int cmp = strncmp(tc->name, tb->name, sizeof (tc->name));
        if (cmp < 0) {
            dbg("tc not present: %.*s", (int) sizeof (tc->name), tc->name);
            tc++;
            continue;
        }

        if (cmp > 0) {
            dbg("tc not sent: %s", tb->name);
            tb++;
            continue;
        }

        tcflag_t* flg = NULL;
        if (tb->thing == TERM_IFLAG) {
            flg = &attr.c_iflag;
        } else if (tb->thing == TERM_OFLAG) {
            flg = &attr.c_oflag;
        } else if (tb->thing == TERM_LFLAG) {
            flg = &attr.c_lflag;
        } else if (tb->thing == TERM_C_CC) {
            if (tc->value == shex_hello->posix_vdisable_value)
                attr.c_cc[tb->value] = _POSIX_VDISABLE;
            else
                attr.c_cc[tb->value] = tc->value;

            dbg("c_cc[%s] = %d", tb->name, tc->value);
        }

        if (flg) {
            if (tc->value) {
                dbg("pty %s: set", tb->name);
                *flg |= tb->value;
            } else {
                dbg("pty %s: reset", tb->name);
                *flg &= ~tb->value;
            }
        }

        tc++;
        tb++;
    }

    xtcsetattr(slave, &attr);
    if (shex_hello->have_ws) {
        dbg("ws %ux%u (%ux%u)",
            shex_hello->ws.row,
            shex_hello->ws.col,
            shex_hello->ws.xpixel,
            shex_hello->ws.ypixel);
        set_window_size(master, &shex_hello->ws);
    }
}

static char**
read_child_arglist(size_t expected)
{
    char** argv;

    if (expected >= SIZE_MAX / sizeof (*argv))
        die(EFBIG, "too many arguments");

    argv = xalloc(sizeof (*argv) * (1+expected));
    for (size_t argno = 0; argno < expected; ++argno) {
        SCOPED_RESLIST(rl_read_arg);
        struct msg_cmdline_argument* m;
        struct msg* mhdr = read_msg(0, read_all_adb_encoded);
        reslist_pop_nodestroy(rl_read_arg);

        const char* argval;
        size_t arglen;

        if (mhdr->type == MSG_CMDLINE_ARGUMENT) {
            m = (struct msg_cmdline_argument*) mhdr;
            if (mhdr->size < sizeof (*m))
                die(ECOMM,
                    "bad handshake: MSG_CMDLINE_ARGUMENT size %u < %u",
                    (unsigned) mhdr->size,
                    (unsigned) sizeof (*m));

            argval = m->value;
            arglen = m->msg.size - sizeof (*m);
        } else if (mhdr->type == MSG_CMDLINE_DEFAULT_SH ||
                   mhdr->type == MSG_CMDLINE_DEFAULT_SH_LOGIN)
        {
            argval = getenv("SHELL");
            if (argval == NULL)
                argval = DEFAULT_SHELL;

            if (mhdr->type == MSG_CMDLINE_DEFAULT_SH_LOGIN)
                argval = xaprintf("-%s", argval);

            arglen = strlen(argval);
        } else {
            die(ECOMM,
                "bad handshake: unknown init msg s=%u t=%u",
                (unsigned) mhdr->size, (unsigned) mhdr->type);
        }

        argv[argno] = xalloc(arglen + 1);
        memcpy(argv[argno], argval, arglen);
        argv[argno][arglen] = '\0';
    }

    argv[expected] = NULL;
    return argv;
}
Пример #10
0
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;
}
Пример #11
0
static void
send_stat_packet(int to_peer, int xfer_fd)
{
    struct stat st;
    if (fstat(xfer_fd, &st) == -1)
        die_errno("fstat");
    struct xfer_msg m = {
        .type = XFER_MSG_STAT,
        .u.stat.atime = st.st_atime,
        .u.stat.mtime = st.st_mtime,
#ifdef HAVE_STRUCT_STAT_ST_ATIM
        .u.stat.atime_ns = st.st_atim.tv_nsec,
#endif
#ifdef HAVE_STRUCT_STAT_ST_MTIM
        .u.stat.mtime_ns = st.st_mtim.tv_nsec,
#endif
        .u.stat.size = st.st_size,
        .u.stat.ugo_bits = st.st_mode & 0777,
    };

    send_xfer_msg(to_peer, &m);
}

struct xfer_ctx {
    int from_peer;
    int to_peer;
    const struct cmd_xfer_stub_info* info;
};

static uint32_t
recv_data_header(int from_peer)
{
    struct xfer_msg m = recv_xfer_msg(from_peer);
    if (m.type != XFER_MSG_DATA)
        die(ECOMM, "unexpected message type %u", (unsigned) m.type);
    return m.u.data.payload_size;
}

static void
send_data_header(int to_peer, uint32_t size)
{
    struct xfer_msg m = {
        .type = XFER_MSG_DATA,
        .u.data.payload_size = size,
    };

    send_xfer_msg(to_peer, &m);
}

static uint64_t
copy_loop_posix_recv(
    int from_peer,
    int dest_fd)
{
    SCOPED_RESLIST(rl);
    struct growable_buffer buf = { 0 };
    uint64_t total_written = 0;
    size_t chunksz;

    do {
        chunksz = recv_data_header(from_peer);
        dbg("data chunk header chunksz=%u", (unsigned) chunksz);
        resize_buffer(&buf, chunksz);
        if (read_all(from_peer, buf.buf, chunksz) != chunksz)
            die(ECOMM, "unexpected EOF");
        write_all(dest_fd, buf.buf, chunksz);
        if (SATADD(&total_written, total_written, chunksz))
            die(ECOMM, "file size too large");
    } while (chunksz > 0);

    return total_written;
}

static void
copy_loop_posix_send(
    int to_peer,
    int source_fd)
{
    SCOPED_RESLIST(rl);
    size_t bufsz = 32 * 1024;
    uint8_t* buf = xalloc(bufsz);
    size_t nr_read;

    assert(bufsz <= UINT32_MAX);

    do {
        nr_read = read_all(source_fd, buf, bufsz);
        send_data_header(to_peer, nr_read);
        write_all(to_peer, buf, nr_read);
    } while (nr_read > 0);
}