Пример #1
0
void
dbglock_init(void)
{
    if (dbglock_fd != -1)
        return;

    if (!dbg_enabled_p())
        return;

    const char envvar[] = "FB_ADB_DBGLOCK_NAME";
    /* No, we can't just inherit the file descriptor.  Without a
     * separate file open, taking the lock won't block.  */

    const char* fn = getenv(envvar);
    if (fn == NULL) {
        const char* pfx = DEFAULT_TEMP_DIR;
        char* tmpfname = xaprintf("%s/fb-adb-dbg-XXXXXX", pfx);
        struct cleanup* cl = cleanup_allocate();
        int tmpfd = mkostemp(tmpfname, O_CLOEXEC);
        if (tmpfd != -1) {
            setenv(envvar, tmpfname, 1);
            cleanup_commit(cl, cleanup_dbginit, tmpfname);
            dbglock_fd = tmpfd;
        }

        return;
    }

    dbglock_fd = open(fn, O_CLOEXEC | O_RDWR);
}
Пример #2
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;
}
Пример #3
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);
    }
}
Пример #4
0
static const char*
xttyname(int fd)
{
#ifndef __ANDROID__
    return ttyname(fd);
#else
    return xreadlink(xaprintf("/proc/self/fd/%d", fd));
#endif
}
Пример #5
0
static char*
make_odex_name(const char* dex_file_name)
{
    char* dex_file_name_copy = xstrdup(dex_file_name);
    char* suffix = strrchr(dex_file_name_copy, '.');
    if (suffix == NULL || strcmp(suffix, ".jar") != 0)
        die(EINVAL, "invalid .jar file name %s", dex_file_name);

    *suffix = '\0';
    return xaprintf("%s.odex", dex_file_name_copy);
}
int
bash_completion_main(const struct cmd_bash_completion_info* info)
{
    const char* fb_adb_binary = orig_argv0;
#ifdef HAVE_REALPATH
    // If argv0 isn't a relative or absolute path, assume we got it
    // from PATH and don't touch it.
    if (strchr(fb_adb_binary, '/'))
        fb_adb_binary = xrealpath(fb_adb_binary);
#endif
    char* argv0_line = xaprintf(
        ": ${_fb_adb:=%s}\n",
        xshellquote(fb_adb_binary));
    write_all(STDOUT_FILENO, argv0_line, strlen(argv0_line));
    write_all(STDOUT_FILENO, bash_completion, sizeof (bash_completion));
    return 0;
}
Пример #7
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\", ...");
}
Пример #8
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;
}
Пример #9
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;
}
Пример #10
0
static void
do_xfer_recv(const struct xfer_opts xfer_opts,
             const char* filename,
             const char* desired_basename,
             int from_peer)
{
    struct xfer_msg statm = recv_xfer_msg(from_peer);
    if (statm.type != XFER_MSG_STAT)
        die(ECOMM, "expected stat msg");

    struct cleanup* error_cl = cleanup_allocate();
    struct stat st;
    const char* parent_directory = NULL;
    const char* rename_to = NULL;
    const char* write_mode = NULL;
    int dest_fd;

    if (stat(filename, &st) == 0) {
        if (S_ISDIR(st.st_mode)) {
            if (desired_basename == NULL)
                die(EISDIR, "\"%s\" is a directory", filename);
            parent_directory = filename;
            filename = xaprintf("%s/%s",
                                parent_directory,
                                desired_basename);
        } else if (S_ISREG(st.st_mode)) {
            if (st.st_nlink > 1)
                write_mode = "inplace";
        } else {
            write_mode = "inplace";
        }
    }

    if (parent_directory == NULL)
        parent_directory = xdirname(filename);

    if (write_mode == NULL)
        write_mode = xfer_opts.write_mode;

    bool atomic;
    bool automatic_mode = false;
    if (write_mode == NULL) {
        automatic_mode = true;
        atomic = true;
    } else if (strcmp(write_mode, "atomic") == 0) {
        atomic = true;
    } else if (strcmp(write_mode, "inplace") == 0) {
        atomic = false;
    } else {
        die(EINVAL, "unknown write mode \"%s\"", write_mode);
    }

    bool regular_file = true;
    bool preallocated = false;
    bool chmod_explicit = false;
    mode_t chmod_explicit_modes = 0;

    if (xfer_opts.preserve) {
        chmod_explicit = true;
        chmod_explicit_modes = statm.u.stat.ugo_bits;
    }

    if (xfer_opts.mode) {
        char* endptr = NULL;
        errno = 0;
        unsigned long omode = strtoul(xfer_opts.mode, &endptr, 8);
        if (errno != 0 || *endptr != '\0' || (omode &~ 0777) != 0)
            die(EINVAL, "invalid mode bits: %s", xfer_opts.mode);
        chmod_explicit = true;
        chmod_explicit_modes = (mode_t) omode;
    }

    mode_t creat_mode = (chmod_explicit_modes ? 0200 : 0666);

    if (atomic) {
        rename_to = filename;
        filename =
            xaprintf("%s.fb-adb-%s",
                     filename,
                     gen_hex_random(ENOUGH_ENTROPY));
        dest_fd = try_xopen(
            filename,
            O_CREAT | O_WRONLY | O_EXCL,
            creat_mode);
        if (dest_fd == -1) {
            if (errno == EACCES && automatic_mode) {
                atomic = false;
                filename = rename_to;
                rename_to = NULL;
            } else {
                die_errno("open(\"%s\")", filename);
            }
        }
    }

    if (!atomic) {
        dest_fd = xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, creat_mode);
        if (!S_ISREG(xfstat(dest_fd).st_mode))
            regular_file = false;
    }

    if (regular_file)
        cleanup_commit(error_cl, unlink_cleanup, filename);

    if (regular_file && statm.u.stat.size > 0)
        preallocated = fallocate_if_supported(
            dest_fd,
            statm.u.stat.size);

    uint64_t total_written = copy_loop_posix_recv(from_peer, dest_fd);

    if (preallocated && total_written < statm.u.stat.size)
        xftruncate(dest_fd, total_written);

    if (xfer_opts.preserve) {
        struct timeval times[2] = {
            { statm.u.stat.atime, statm.u.stat.atime_ns / 1000 },
            { statm.u.stat.mtime, statm.u.stat.mtime_ns / 1000 },
        };
#ifdef HAVE_FUTIMES
        if (futimes(dest_fd, times) == -1)
            die_errno("futimes");
#else
        if (utimes(filename, times) == -1)
            die_errno("times");
#endif
    }

    if (chmod_explicit)
        if (fchmod(dest_fd, chmod_explicit_modes) == -1)
            die_errno("fchmod");

    if (xfer_opts.sync)
        xfsync(dest_fd);

    if (rename_to)
        xrename(filename, rename_to);

    if (xfer_opts.sync)
        xfsync(xopen(parent_directory, O_DIRECTORY|O_RDONLY, 0));

    cleanup_forget(error_cl);
}