mirror of
				https://github.com/multirom-nexus6p/multirom_adbd.git
				synced 2025-11-04 13:45:34 +08:00 
			
		
		
		
	Taken from system/core/adb on the omnirom/android-6.0 branch (2b143b247a69deba1577dd1b2ca92138be2b48dc)
		
			
				
	
	
		
			696 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			696 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c
 | 
						|
**
 | 
						|
** Copyright 2006, Brian Swetland <swetland@frotz.net>
 | 
						|
**
 | 
						|
** Licensed under the Apache License, Version 2.0 (the "License"); 
 | 
						|
** you may not use this file except in compliance with the License. 
 | 
						|
** You may obtain a copy of the License at 
 | 
						|
**
 | 
						|
**     http://www.apache.org/licenses/LICENSE-2.0 
 | 
						|
**
 | 
						|
** Unless required by applicable law or agreed to in writing, software 
 | 
						|
** distributed under the License is distributed on an "AS IS" BASIS, 
 | 
						|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 | 
						|
** See the License for the specific language governing permissions and 
 | 
						|
** limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
#define TRACE_TAG TRACE_FDEVENT
 | 
						|
 | 
						|
#include "sysdeps.h"
 | 
						|
#include "fdevent.h"
 | 
						|
 | 
						|
#include <errno.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stddef.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <sys/ioctl.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include "adb_io.h"
 | 
						|
#include "adb_trace.h"
 | 
						|
 | 
						|
/* !!! Do not enable DEBUG for the adb that will run as the server:
 | 
						|
** both stdout and stderr are used to communicate between the client
 | 
						|
** and server. Any extra output will cause failures.
 | 
						|
*/
 | 
						|
#define DEBUG 0   /* non-0 will break adb server */
 | 
						|
 | 
						|
// This socket is used when a subproc shell service exists.
 | 
						|
// It wakes up the fdevent_loop() and cause the correct handling
 | 
						|
// of the shell's pseudo-tty master. I.e. force close it.
 | 
						|
int SHELL_EXIT_NOTIFY_FD = -1;
 | 
						|
 | 
						|
static void fatal(const char *fn, const char *fmt, ...)
 | 
						|
{
 | 
						|
    va_list ap;
 | 
						|
    va_start(ap, fmt);
 | 
						|
    fprintf(stderr, "%s:", fn);
 | 
						|
    vfprintf(stderr, fmt, ap);
 | 
						|
    va_end(ap);
 | 
						|
    abort();
 | 
						|
}
 | 
						|
 | 
						|
#define FATAL(x...) fatal(__FUNCTION__, x)
 | 
						|
 | 
						|
#if DEBUG
 | 
						|
static void dump_fde(fdevent *fde, const char *info)
 | 
						|
{
 | 
						|
    adb_mutex_lock(&D_lock);
 | 
						|
    fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
 | 
						|
            fde->state & FDE_READ ? 'R' : ' ',
 | 
						|
            fde->state & FDE_WRITE ? 'W' : ' ',
 | 
						|
            fde->state & FDE_ERROR ? 'E' : ' ',
 | 
						|
            info);
 | 
						|
    adb_mutex_unlock(&D_lock);
 | 
						|
}
 | 
						|
#else
 | 
						|
#define dump_fde(fde, info) do { } while(0)
 | 
						|
#endif
 | 
						|
 | 
						|
#define FDE_EVENTMASK  0x00ff
 | 
						|
#define FDE_STATEMASK  0xff00
 | 
						|
 | 
						|
#define FDE_ACTIVE     0x0100
 | 
						|
#define FDE_PENDING    0x0200
 | 
						|
#define FDE_CREATED    0x0400
 | 
						|
 | 
						|
static void fdevent_plist_enqueue(fdevent *node);
 | 
						|
static void fdevent_plist_remove(fdevent *node);
 | 
						|
static fdevent *fdevent_plist_dequeue(void);
 | 
						|
static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata);
 | 
						|
 | 
						|
static fdevent list_pending = {
 | 
						|
    .next = &list_pending,
 | 
						|
    .prev = &list_pending,
 | 
						|
    .fd = -1,
 | 
						|
    .force_eof = 0,
 | 
						|
    .state = 0,
 | 
						|
    .events = 0,
 | 
						|
    .func = nullptr,
 | 
						|
    .arg = nullptr,
 | 
						|
};
 | 
						|
 | 
						|
static fdevent **fd_table = 0;
 | 
						|
static int fd_table_max = 0;
 | 
						|
 | 
						|
#ifdef CRAPTASTIC
 | 
						|
//HAVE_EPOLL
 | 
						|
 | 
						|
#include <sys/epoll.h>
 | 
						|
 | 
						|
static int epoll_fd = -1;
 | 
						|
 | 
						|
static void fdevent_init()
 | 
						|
{
 | 
						|
        /* XXX: what's a good size for the passed in hint? */
 | 
						|
    epoll_fd = epoll_create(256);
 | 
						|
 | 
						|
    if(epoll_fd < 0) {
 | 
						|
        perror("epoll_create() failed");
 | 
						|
        exit(1);
 | 
						|
    }
 | 
						|
 | 
						|
        /* mark for close-on-exec */
 | 
						|
    fcntl(epoll_fd, F_SETFD, FD_CLOEXEC);
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_connect(fdevent *fde)
 | 
						|
{
 | 
						|
    struct epoll_event ev;
 | 
						|
 | 
						|
    memset(&ev, 0, sizeof(ev));
 | 
						|
    ev.events = 0;
 | 
						|
    ev.data.ptr = fde;
 | 
						|
 | 
						|
#if 0
 | 
						|
    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
 | 
						|
        perror("epoll_ctl() failed\n");
 | 
						|
        exit(1);
 | 
						|
    }
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_disconnect(fdevent *fde)
 | 
						|
{
 | 
						|
    struct epoll_event ev;
 | 
						|
 | 
						|
    memset(&ev, 0, sizeof(ev));
 | 
						|
    ev.events = 0;
 | 
						|
    ev.data.ptr = fde;
 | 
						|
 | 
						|
        /* technically we only need to delete if we
 | 
						|
        ** were actively monitoring events, but let's
 | 
						|
        ** be aggressive and do it anyway, just in case
 | 
						|
        ** something's out of sync
 | 
						|
        */
 | 
						|
    epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev);
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_update(fdevent *fde, unsigned events)
 | 
						|
{
 | 
						|
    struct epoll_event ev;
 | 
						|
    int active;
 | 
						|
 | 
						|
    active = (fde->state & FDE_EVENTMASK) != 0;
 | 
						|
 | 
						|
    memset(&ev, 0, sizeof(ev));
 | 
						|
    ev.events = 0;
 | 
						|
    ev.data.ptr = fde;
 | 
						|
 | 
						|
    if(events & FDE_READ) ev.events |= EPOLLIN;
 | 
						|
    if(events & FDE_WRITE) ev.events |= EPOLLOUT;
 | 
						|
    if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP);
 | 
						|
 | 
						|
    fde->state = (fde->state & FDE_STATEMASK) | events;
 | 
						|
 | 
						|
    if(active) {
 | 
						|
            /* we're already active. if we're changing to *no*
 | 
						|
            ** events being monitored, we need to delete, otherwise
 | 
						|
            ** we need to just modify
 | 
						|
            */
 | 
						|
        if(ev.events) {
 | 
						|
            if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) {
 | 
						|
                perror("epoll_ctl() failed\n");
 | 
						|
                exit(1);
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) {
 | 
						|
                perror("epoll_ctl() failed\n");
 | 
						|
                exit(1);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
            /* we're not active.  if we're watching events, we need
 | 
						|
            ** to add, otherwise we can just do nothing
 | 
						|
            */
 | 
						|
        if(ev.events) {
 | 
						|
            if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) {
 | 
						|
                perror("epoll_ctl() failed\n");
 | 
						|
                exit(1);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_process()
 | 
						|
{
 | 
						|
    struct epoll_event events[256];
 | 
						|
    fdevent *fde;
 | 
						|
    int i, n;
 | 
						|
 | 
						|
    n = epoll_wait(epoll_fd, events, 256, -1);
 | 
						|
 | 
						|
    if(n < 0) {
 | 
						|
        if(errno == EINTR) return;
 | 
						|
        perror("epoll_wait");
 | 
						|
        exit(1);
 | 
						|
    }
 | 
						|
 | 
						|
    for(i = 0; i < n; i++) {
 | 
						|
        struct epoll_event *ev = events + i;
 | 
						|
        fde = ev->data.ptr;
 | 
						|
 | 
						|
        if(ev->events & EPOLLIN) {
 | 
						|
            fde->events |= FDE_READ;
 | 
						|
        }
 | 
						|
        if(ev->events & EPOLLOUT) {
 | 
						|
            fde->events |= FDE_WRITE;
 | 
						|
        }
 | 
						|
        if(ev->events & (EPOLLERR | EPOLLHUP)) {
 | 
						|
            fde->events |= FDE_ERROR;
 | 
						|
        }
 | 
						|
        if(fde->events) {
 | 
						|
            if(fde->state & FDE_PENDING) continue;
 | 
						|
            fde->state |= FDE_PENDING;
 | 
						|
            fdevent_plist_enqueue(fde);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#else /* USE_SELECT */
 | 
						|
 | 
						|
#ifdef HAVE_WINSOCK
 | 
						|
#include <winsock2.h>
 | 
						|
#else
 | 
						|
#include <sys/select.h>
 | 
						|
#endif
 | 
						|
 | 
						|
static fd_set read_fds;
 | 
						|
static fd_set write_fds;
 | 
						|
static fd_set error_fds;
 | 
						|
 | 
						|
static int select_n = 0;
 | 
						|
 | 
						|
static void fdevent_init(void)
 | 
						|
{
 | 
						|
    FD_ZERO(&read_fds);
 | 
						|
    FD_ZERO(&write_fds);
 | 
						|
    FD_ZERO(&error_fds);
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_connect(fdevent *fde)
 | 
						|
{
 | 
						|
    if(fde->fd >= select_n) {
 | 
						|
        select_n = fde->fd + 1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_disconnect(fdevent *fde)
 | 
						|
{
 | 
						|
    int i, n;
 | 
						|
 | 
						|
    FD_CLR(fde->fd, &read_fds);
 | 
						|
    FD_CLR(fde->fd, &write_fds);
 | 
						|
    FD_CLR(fde->fd, &error_fds);
 | 
						|
 | 
						|
    for(n = 0, i = 0; i < select_n; i++) {
 | 
						|
        if(fd_table[i] != 0) n = i;
 | 
						|
    }
 | 
						|
    select_n = n + 1;
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_update(fdevent *fde, unsigned events)
 | 
						|
{
 | 
						|
    if(events & FDE_READ) {
 | 
						|
        FD_SET(fde->fd, &read_fds);
 | 
						|
    } else {
 | 
						|
        FD_CLR(fde->fd, &read_fds);
 | 
						|
    }
 | 
						|
    if(events & FDE_WRITE) {
 | 
						|
        FD_SET(fde->fd, &write_fds);
 | 
						|
    } else {
 | 
						|
        FD_CLR(fde->fd, &write_fds);
 | 
						|
    }
 | 
						|
    if(events & FDE_ERROR) {
 | 
						|
        FD_SET(fde->fd, &error_fds);
 | 
						|
    } else {
 | 
						|
        FD_CLR(fde->fd, &error_fds);
 | 
						|
    }
 | 
						|
 | 
						|
    fde->state = (fde->state & FDE_STATEMASK) | events;
 | 
						|
}
 | 
						|
 | 
						|
/* Looks at fd_table[] for bad FDs and sets bit in fds.
 | 
						|
** Returns the number of bad FDs.
 | 
						|
*/
 | 
						|
static int fdevent_fd_check(fd_set *fds)
 | 
						|
{
 | 
						|
    int i, n = 0;
 | 
						|
    fdevent *fde;
 | 
						|
 | 
						|
    for(i = 0; i < select_n; i++) {
 | 
						|
        fde = fd_table[i];
 | 
						|
        if(fde == 0) continue;
 | 
						|
        if(fcntl(i, F_GETFL, NULL) < 0) {
 | 
						|
            FD_SET(i, fds);
 | 
						|
            n++;
 | 
						|
            // fde->state |= FDE_DONT_CLOSE;
 | 
						|
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return n;
 | 
						|
}
 | 
						|
 | 
						|
#if !DEBUG
 | 
						|
static inline void dump_all_fds(const char* /* extra_msg */) {}
 | 
						|
#else
 | 
						|
static void dump_all_fds(const char *extra_msg)
 | 
						|
{
 | 
						|
int i;
 | 
						|
    fdevent *fde;
 | 
						|
    // per fd: 4 digits (but really: log10(FD_SETSIZE)), 1 staus, 1 blank
 | 
						|
    char msg_buff[FD_SETSIZE*6 + 1], *pb=msg_buff;
 | 
						|
    size_t max_chars = FD_SETSIZE * 6 + 1;
 | 
						|
    int printed_out;
 | 
						|
#define SAFE_SPRINTF(...)                                                    \
 | 
						|
    do {                                                                     \
 | 
						|
        printed_out = snprintf(pb, max_chars, __VA_ARGS__);                  \
 | 
						|
        if (printed_out <= 0) {                                              \
 | 
						|
            D("... snprintf failed.\n");                                     \
 | 
						|
            return;                                                          \
 | 
						|
        }                                                                    \
 | 
						|
        if (max_chars < (unsigned int)printed_out) {                         \
 | 
						|
            D("... snprintf out of space.\n");                               \
 | 
						|
            return;                                                          \
 | 
						|
        }                                                                    \
 | 
						|
        pb += printed_out;                                                   \
 | 
						|
        max_chars -= printed_out;                                            \
 | 
						|
    } while(0)
 | 
						|
 | 
						|
    for(i = 0; i < select_n; i++) {
 | 
						|
        fde = fd_table[i];
 | 
						|
        SAFE_SPRINTF("%d", i);
 | 
						|
        if(fde == 0) {
 | 
						|
            SAFE_SPRINTF("? ");
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        if(fcntl(i, F_GETFL, NULL) < 0) {
 | 
						|
            SAFE_SPRINTF("b");
 | 
						|
        }
 | 
						|
        SAFE_SPRINTF(" ");
 | 
						|
    }
 | 
						|
    D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static void fdevent_process()
 | 
						|
{
 | 
						|
    int i, n;
 | 
						|
    fdevent *fde;
 | 
						|
    unsigned events;
 | 
						|
    fd_set rfd, wfd, efd;
 | 
						|
 | 
						|
    memcpy(&rfd, &read_fds, sizeof(fd_set));
 | 
						|
    memcpy(&wfd, &write_fds, sizeof(fd_set));
 | 
						|
    memcpy(&efd, &error_fds, sizeof(fd_set));
 | 
						|
 | 
						|
    dump_all_fds("pre select()");
 | 
						|
 | 
						|
    n = select(select_n, &rfd, &wfd, &efd, NULL);
 | 
						|
    int saved_errno = errno;
 | 
						|
    D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
 | 
						|
 | 
						|
    dump_all_fds("post select()");
 | 
						|
 | 
						|
    if(n < 0) {
 | 
						|
        switch(saved_errno) {
 | 
						|
        case EINTR: return;
 | 
						|
        case EBADF:
 | 
						|
            // Can't trust the FD sets after an error.
 | 
						|
            FD_ZERO(&wfd);
 | 
						|
            FD_ZERO(&efd);
 | 
						|
            FD_ZERO(&rfd);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            D("Unexpected select() error=%d\n", saved_errno);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if(n <= 0) {
 | 
						|
        // We fake a read, as the rest of the code assumes
 | 
						|
        // that errors will be detected at that point.
 | 
						|
        n = fdevent_fd_check(&rfd);
 | 
						|
    }
 | 
						|
 | 
						|
    for(i = 0; (i < select_n) && (n > 0); i++) {
 | 
						|
        events = 0;
 | 
						|
        if(FD_ISSET(i, &rfd)) { events |= FDE_READ; n--; }
 | 
						|
        if(FD_ISSET(i, &wfd)) { events |= FDE_WRITE; n--; }
 | 
						|
        if(FD_ISSET(i, &efd)) { events |= FDE_ERROR; n--; }
 | 
						|
 | 
						|
        if(events) {
 | 
						|
            fde = fd_table[i];
 | 
						|
            if(fde == 0)
 | 
						|
              FATAL("missing fde for fd %d\n", i);
 | 
						|
 | 
						|
            fde->events |= events;
 | 
						|
 | 
						|
            D("got events fde->fd=%d events=%04x, state=%04x\n",
 | 
						|
                fde->fd, fde->events, fde->state);
 | 
						|
            if(fde->state & FDE_PENDING) continue;
 | 
						|
            fde->state |= FDE_PENDING;
 | 
						|
            fdevent_plist_enqueue(fde);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
static void fdevent_register(fdevent *fde)
 | 
						|
{
 | 
						|
    if(fde->fd < 0) {
 | 
						|
        FATAL("bogus negative fd (%d)\n", fde->fd);
 | 
						|
    }
 | 
						|
 | 
						|
    if(fde->fd >= fd_table_max) {
 | 
						|
        int oldmax = fd_table_max;
 | 
						|
        if(fde->fd > 32000) {
 | 
						|
            FATAL("bogus huuuuge fd (%d)\n", fde->fd);
 | 
						|
        }
 | 
						|
        if(fd_table_max == 0) {
 | 
						|
            fdevent_init();
 | 
						|
            fd_table_max = 256;
 | 
						|
        }
 | 
						|
        while(fd_table_max <= fde->fd) {
 | 
						|
            fd_table_max *= 2;
 | 
						|
        }
 | 
						|
        fd_table = reinterpret_cast<fdevent**>(
 | 
						|
            realloc(fd_table, sizeof(fdevent*) * fd_table_max));
 | 
						|
        if(fd_table == 0) {
 | 
						|
            FATAL("could not expand fd_table to %d entries\n", fd_table_max);
 | 
						|
        }
 | 
						|
        memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
 | 
						|
    }
 | 
						|
 | 
						|
    fd_table[fde->fd] = fde;
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_unregister(fdevent *fde)
 | 
						|
{
 | 
						|
    if((fde->fd < 0) || (fde->fd >= fd_table_max)) {
 | 
						|
        FATAL("fd out of range (%d)\n", fde->fd);
 | 
						|
    }
 | 
						|
 | 
						|
    if(fd_table[fde->fd] != fde) {
 | 
						|
        FATAL("fd_table out of sync [%d]\n", fde->fd);
 | 
						|
    }
 | 
						|
 | 
						|
    fd_table[fde->fd] = 0;
 | 
						|
 | 
						|
    if(!(fde->state & FDE_DONT_CLOSE)) {
 | 
						|
        dump_fde(fde, "close");
 | 
						|
        adb_close(fde->fd);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_plist_enqueue(fdevent *node)
 | 
						|
{
 | 
						|
    fdevent *list = &list_pending;
 | 
						|
 | 
						|
    node->next = list;
 | 
						|
    node->prev = list->prev;
 | 
						|
    node->prev->next = node;
 | 
						|
    list->prev = node;
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_plist_remove(fdevent *node)
 | 
						|
{
 | 
						|
    node->prev->next = node->next;
 | 
						|
    node->next->prev = node->prev;
 | 
						|
    node->next = 0;
 | 
						|
    node->prev = 0;
 | 
						|
}
 | 
						|
 | 
						|
static fdevent *fdevent_plist_dequeue(void)
 | 
						|
{
 | 
						|
    fdevent *list = &list_pending;
 | 
						|
    fdevent *node = list->next;
 | 
						|
 | 
						|
    if(node == list) return 0;
 | 
						|
 | 
						|
    list->next = node->next;
 | 
						|
    list->next->prev = list;
 | 
						|
    node->next = 0;
 | 
						|
    node->prev = 0;
 | 
						|
 | 
						|
    return node;
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_call_fdfunc(fdevent* fde)
 | 
						|
{
 | 
						|
    unsigned events = fde->events;
 | 
						|
    fde->events = 0;
 | 
						|
    if(!(fde->state & FDE_PENDING)) return;
 | 
						|
    fde->state &= (~FDE_PENDING);
 | 
						|
    dump_fde(fde, "callback");
 | 
						|
    fde->func(fde->fd, events, fde->arg);
 | 
						|
}
 | 
						|
 | 
						|
static void fdevent_subproc_event_func(int fd, unsigned ev,
 | 
						|
                                       void* /* userdata */)
 | 
						|
{
 | 
						|
 | 
						|
    D("subproc handling on fd=%d ev=%04x\n", fd, ev);
 | 
						|
 | 
						|
    // Hook oneself back into the fde's suitable for select() on read.
 | 
						|
    if((fd < 0) || (fd >= fd_table_max)) {
 | 
						|
        FATAL("fd %d out of range for fd_table \n", fd);
 | 
						|
    }
 | 
						|
    fdevent *fde = fd_table[fd];
 | 
						|
    fdevent_add(fde, FDE_READ);
 | 
						|
 | 
						|
    if(ev & FDE_READ){
 | 
						|
      int subproc_fd;
 | 
						|
 | 
						|
      if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
 | 
						|
          FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
 | 
						|
      }
 | 
						|
      if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
 | 
						|
          D("subproc_fd %d out of range 0, fd_table_max=%d\n",
 | 
						|
            subproc_fd, fd_table_max);
 | 
						|
          return;
 | 
						|
      }
 | 
						|
      fdevent *subproc_fde = fd_table[subproc_fd];
 | 
						|
      if(!subproc_fde) {
 | 
						|
          D("subproc_fd %d cleared from fd_table\n", subproc_fd);
 | 
						|
          return;
 | 
						|
      }
 | 
						|
      if(subproc_fde->fd != subproc_fd) {
 | 
						|
          // Already reallocated?
 | 
						|
          D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
 | 
						|
          return;
 | 
						|
      }
 | 
						|
 | 
						|
      subproc_fde->force_eof = 1;
 | 
						|
 | 
						|
      int rcount = 0;
 | 
						|
      ioctl(subproc_fd, FIONREAD, &rcount);
 | 
						|
      D("subproc with fd=%d  has rcount=%d err=%d\n",
 | 
						|
        subproc_fd, rcount, errno);
 | 
						|
 | 
						|
      if(rcount) {
 | 
						|
        // If there is data left, it will show up in the select().
 | 
						|
        // This works because there is no other thread reading that
 | 
						|
        // data when in this fd_func().
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      D("subproc_fde.state=%04x\n", subproc_fde->state);
 | 
						|
      subproc_fde->events |= FDE_READ;
 | 
						|
      if(subproc_fde->state & FDE_PENDING) {
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      subproc_fde->state |= FDE_PENDING;
 | 
						|
      fdevent_call_fdfunc(subproc_fde);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
fdevent *fdevent_create(int fd, fd_func func, void *arg)
 | 
						|
{
 | 
						|
    fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
 | 
						|
    if(fde == 0) return 0;
 | 
						|
    fdevent_install(fde, fd, func, arg);
 | 
						|
    fde->state |= FDE_CREATED;
 | 
						|
    return fde;
 | 
						|
}
 | 
						|
 | 
						|
void fdevent_destroy(fdevent *fde)
 | 
						|
{
 | 
						|
    if(fde == 0) return;
 | 
						|
    if(!(fde->state & FDE_CREATED)) {
 | 
						|
        FATAL("fde %p not created by fdevent_create()\n", fde);
 | 
						|
    }
 | 
						|
    fdevent_remove(fde);
 | 
						|
    free(fde);
 | 
						|
}
 | 
						|
 | 
						|
void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
 | 
						|
{
 | 
						|
    memset(fde, 0, sizeof(fdevent));
 | 
						|
    fde->state = FDE_ACTIVE;
 | 
						|
    fde->fd = fd;
 | 
						|
    fde->force_eof = 0;
 | 
						|
    fde->func = func;
 | 
						|
    fde->arg = arg;
 | 
						|
 | 
						|
#ifndef HAVE_WINSOCK
 | 
						|
    fcntl(fd, F_SETFL, O_NONBLOCK);
 | 
						|
#endif
 | 
						|
    fdevent_register(fde);
 | 
						|
    dump_fde(fde, "connect");
 | 
						|
    fdevent_connect(fde);
 | 
						|
    fde->state |= FDE_ACTIVE;
 | 
						|
}
 | 
						|
 | 
						|
void fdevent_remove(fdevent *fde)
 | 
						|
{
 | 
						|
    if(fde->state & FDE_PENDING) {
 | 
						|
        fdevent_plist_remove(fde);
 | 
						|
    }
 | 
						|
 | 
						|
    if(fde->state & FDE_ACTIVE) {
 | 
						|
        fdevent_disconnect(fde);
 | 
						|
        dump_fde(fde, "disconnect");
 | 
						|
        fdevent_unregister(fde);
 | 
						|
    }
 | 
						|
 | 
						|
    fde->state = 0;
 | 
						|
    fde->events = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void fdevent_set(fdevent *fde, unsigned events)
 | 
						|
{
 | 
						|
    events &= FDE_EVENTMASK;
 | 
						|
 | 
						|
    if((fde->state & FDE_EVENTMASK) == events) return;
 | 
						|
 | 
						|
    if(fde->state & FDE_ACTIVE) {
 | 
						|
        fdevent_update(fde, events);
 | 
						|
        dump_fde(fde, "update");
 | 
						|
    }
 | 
						|
 | 
						|
    fde->state = (fde->state & FDE_STATEMASK) | events;
 | 
						|
 | 
						|
    if(fde->state & FDE_PENDING) {
 | 
						|
            /* if we're pending, make sure
 | 
						|
            ** we don't signal an event that
 | 
						|
            ** is no longer wanted.
 | 
						|
            */
 | 
						|
        fde->events &= (~events);
 | 
						|
        if(fde->events == 0) {
 | 
						|
            fdevent_plist_remove(fde);
 | 
						|
            fde->state &= (~FDE_PENDING);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void fdevent_add(fdevent *fde, unsigned events)
 | 
						|
{
 | 
						|
    fdevent_set(
 | 
						|
        fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
 | 
						|
}
 | 
						|
 | 
						|
void fdevent_del(fdevent *fde, unsigned events)
 | 
						|
{
 | 
						|
    fdevent_set(
 | 
						|
        fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
 | 
						|
}
 | 
						|
 | 
						|
void fdevent_subproc_setup()
 | 
						|
{
 | 
						|
    int s[2];
 | 
						|
 | 
						|
    if(adb_socketpair(s)) {
 | 
						|
        FATAL("cannot create shell-exit socket-pair\n");
 | 
						|
    }
 | 
						|
    D("socketpair: (%d,%d)", s[0], s[1]);
 | 
						|
 | 
						|
    SHELL_EXIT_NOTIFY_FD = s[0];
 | 
						|
    fdevent *fde;
 | 
						|
    fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
 | 
						|
    if(!fde)
 | 
						|
      FATAL("cannot create fdevent for shell-exit handler\n");
 | 
						|
    fdevent_add(fde, FDE_READ);
 | 
						|
}
 | 
						|
 | 
						|
void fdevent_loop()
 | 
						|
{
 | 
						|
    fdevent *fde;
 | 
						|
    fdevent_subproc_setup();
 | 
						|
 | 
						|
    for(;;) {
 | 
						|
        D("--- ---- waiting for events\n");
 | 
						|
 | 
						|
        fdevent_process();
 | 
						|
 | 
						|
        while((fde = fdevent_plist_dequeue())) {
 | 
						|
            fdevent_call_fdfunc(fde);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |