static void *output_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; ADB_LOGD(ADB_TSPT, "%s: starting transport output thread on fd %d, SYNC online (%d)", t->serial, t->fd, t->sync_token + 1); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 1; p->msg.arg1 = ++(t->sync_token); p->msg.magic = A_SYNC ^ 0xffffffff; if (write_packet(t->fd, t->serial, &p)) { put_apacket(p); ADB_LOGE(ADB_TSPT, "%s: failed to write SYNC packet", t->serial); goto oops; } ADB_LOGD(ADB_TSPT, "%s: data pump started", t->serial); for (;;) { p = get_apacket(); if (t->read_from_remote(p, t) == 0) { ADB_LOGD(ADB_TSPT, "%s: received remote packet, sending to transport", t->serial); if (write_packet(t->fd, t->serial, &p)) { put_apacket(p); ADB_LOGE(ADB_TSPT, "%s: failed to write apacket to transport", t->serial); goto oops; } } else { ADB_LOGE(ADB_TSPT, "%s: remote read failed for transport", t->serial); put_apacket(p); break; } } ADB_LOGD(ADB_TSPT, "%s: SYNC offline for transport", t->serial); p = get_apacket(); p->msg.command = A_SYNC; p->msg.arg0 = 0; p->msg.arg1 = 0; p->msg.magic = A_SYNC ^ 0xffffffff; if (write_packet(t->fd, t->serial, &p)) { put_apacket(p); ADB_LOGW(ADB_TSPT, "%s: failed to write SYNC apacket to transport", t->serial); } oops: ADB_LOGD(ADB_TSPT, "%s: transport output thread is exiting", t->serial); kick_transport(t); transport_unref(t); return 0; }
static void dump_packet(const char* name, const char* func, apacket* p) { unsigned command = p->msg.command; int len = p->msg.data_length; char cmd[9]; char arg0[12], arg1[12]; int n; for (n = 0; n < 4; n++) { int b = (command >> (n*8)) & 255; if (b < 32 || b >= 127) break; cmd[n] = (char)b; } if (n == 4) { cmd[4] = 0; } else { /* There is some non-ASCII name in the command, so dump * the hexadecimal value instead */ snprintf(cmd, sizeof cmd, "%08x", command); } if (p->msg.arg0 < 256U) snprintf(arg0, sizeof arg0, "%d", p->msg.arg0); else snprintf(arg0, sizeof arg0, "0x%x", p->msg.arg0); if (p->msg.arg1 < 256U) snprintf(arg1, sizeof arg1, "%d", p->msg.arg1); else snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1); ADB_LOGD(ADB_TSPT, "%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name, func, cmd, arg0, arg1, len); dump_hex(p->data, len); }
static void transport_unref_locked(atransport *t) { t->ref_count--; if (t->ref_count == 0) { ADB_LOGD(ADB_TSPT, "transport: %s unref (kicking and closing)", t->serial); if (!t->kicked) { t->kicked = 1; t->kick(t); } t->close(t); remove_transport(t); } else { ADB_LOGD(ADB_TSPT, "transport: %s unref (count=%d)", t->serial, t->ref_count); } }
static void dump_fde(fdevent *fde, const char *info) { pthread_mutex_lock(&D_lock); ADB_LOGD(ADB_FDEV, "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); pthread_mutex_unlock(&D_lock); }
static void remove_transport(atransport *transport) { tmsg m; m.transport = transport; m.action = 0; ADB_LOGD(ADB_TSPT, "transport: %s removed", transport->serial); if (transport_write_action(transport_registration_send, &m)) { fatal_errno("cannot write transport registration socket"); } }
// Each atransport contains a list of adisconnects (t->disconnects). // An adisconnect contains a link to the next/prev adisconnect, a function // pointer to a disconnect callback which takes a void* piece of user data and // the atransport, and some user data for the callback (helpfully named // "opaque"). // // The list is circular. New items are added to the entry member of the list // (t->disconnects) by add_transport_disconnect. // // run_transport_disconnects invokes each function in the list. // // Gotchas: // * run_transport_disconnects assumes that t->disconnects is non-null, so // this can't be run on a zeroed atransport. // * The callbacks in this list are not removed when called, and this function // is not guarded against running more than once. As such, ensure that this // function is not called multiple times on the same atransport. // TODO(danalbert): Just fix this so that it is guarded once you have tests. void run_transport_disconnects(atransport* t) { adisconnect* dis = t->disconnects.next; ADB_LOGD(ADB_TSPT, "%s: run_transport_disconnects", t->serial); while (dis != &t->disconnects) { adisconnect* next = dis->next; dis->func(dis->opaque, t); dis = next; } }
static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) { ADB_LOGD(ADB_FDEV, "subproc handling on fd=%d ev=%04x", 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", 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", fd); } if ((subproc_fd < 0) || (subproc_fd >= fd_table_max)) { ADB_LOGD(ADB_FDEV, "subproc_fd %d out of range 0, fd_table_max=%d", subproc_fd, fd_table_max); return; } fdevent *subproc_fde = fd_table[subproc_fd]; if (!subproc_fde) { ADB_LOGD(ADB_FDEV, "subproc_fd %d cleared from fd_table", subproc_fd); return; } if (subproc_fde->fd != subproc_fd) { // Already reallocated? ADB_LOGD(ADB_FDEV, "subproc_fd %d != fd_table[].fd %d", subproc_fd, subproc_fde->fd); return; } subproc_fde->force_eof = 1; int rcount = 0; ioctl(subproc_fd, FIONREAD, &rcount); ADB_LOGD(ADB_FDEV, "subproc with fd=%d has rcount=%d err=%d", 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; } ADB_LOGD(ADB_FDEV, "subproc_fde.state=%04x", 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); } }
static void *input_thread(void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); apacket *p; int active = 0; ADB_LOGD(ADB_TSPT, "%s: starting transport input thread, reading from fd %d", t->serial, t->fd); for (;;) { if (read_packet(t->fd, t->serial, &p)) { ADB_LOGE(ADB_TSPT, "%s: failed to read apacket from transport on fd %d", t->serial, t->fd); break; } if (p->msg.command == A_SYNC) { if (p->msg.arg0 == 0) { ADB_LOGE(ADB_TSPT, "%s: transport SYNC offline", t->serial); put_apacket(p); break; } else { if (p->msg.arg1 == t->sync_token) { ADB_LOGD(ADB_TSPT, "%s: transport SYNC online", t->serial); active = 1; } else { ADB_LOGD(ADB_TSPT, "%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token); } } } else { if (active) { ADB_LOGD(ADB_TSPT, "%s: transport got packet, sending to remote", t->serial); t->write_to_remote(p, t); } else { ADB_LOGD(ADB_TSPT, "%s: transport ignoring packet while offline", t->serial); } } put_apacket(p); } // this is necessary to avoid a race condition that occured when a transport closes // while a client socket is still active. close_all_sockets(t); ADB_LOGD(ADB_TSPT, "%s: transport input thread is exiting, fd %d", t->serial, t->fd); kick_transport(t); transport_unref(t); return 0; }
void fdevent_loop() { fdevent *fde; fdevent_subproc_setup(); for (;;) { ADB_LOGD(ADB_FDEV, "--- ---- waiting for events"); fdevent_process(); while ((fde = fdevent_plist_dequeue())) { fdevent_call_fdfunc(fde); } } }
static void transport_socket_events(int fd, unsigned events, void *_t) { atransport *t = reinterpret_cast<atransport*>(_t); ADB_LOGD(ADB_TSPT, "transport_socket_events(fd=%d, events=%04x,...)", fd, events); if (events & FDE_READ) { apacket *p = 0; if (read_packet(fd, t->serial, &p)) { ADB_LOGE(ADB_TSPT, "%s: failed to read packet from transport socket on fd %d", t->serial, fd); } else { handle_packet(p, (atransport *) _t); } } }
void fdevent_subproc_setup() { int s[2]; if (adb_socketpair(s)) { FATAL("cannot create shell-exit socket-pair"); } ADB_LOGD(ADB_FDEV, "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"); } fdevent_add(fde, FDE_READ); }
void init_transport_registration(void) { int s[2]; if (adb_socketpair(s)) { fatal_errno("cannot open transport registration socketpair"); } ADB_LOGD(ADB_TSPT, "socketpair: (%d,%d)", s[0], s[1]); transport_registration_send = s[0]; transport_registration_recv = s[1]; fdevent_install(&transport_registration_fde, transport_registration_recv, transport_registration_func, 0); fdevent_set(&transport_registration_fde, FDE_READ); }
void dump_hex(const void* data, size_t byte_count) { byte_count = std::min(byte_count, size_t(16)); const uint8_t* p = reinterpret_cast<const uint8_t*>(data); std::string line; for (size_t i = 0; i < byte_count; ++i) { line += mb::util::format("%02x", p[i]); } line.push_back(' '); for (size_t i = 0; i < byte_count; ++i) { int c = p[i]; if (c < 32 || c > 127) { c = '.'; } line.push_back(c); } ADB_LOGD(ADB_LLIO, "%s", line.c_str()); }
void register_usb_transport(usb_handle *usb, const char *serial, const char *devpath, unsigned writeable) { atransport *t = reinterpret_cast<atransport*>(calloc(1, sizeof(atransport))); if (t == nullptr) fatal("cannot allocate USB atransport"); ADB_LOGD(ADB_TSPT, "transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : ""); init_usb_transport(t, usb, (writeable ? CS_OFFLINE : CS_NOPERM)); if (serial) { t->serial = strdup(serial); } if (devpath) { t->devpath = strdup(devpath); } pthread_mutex_lock(&transport_lock); t->next = &pending_list; t->prev = pending_list.prev; t->next->prev = t; t->prev->next = t; pthread_mutex_unlock(&transport_lock); register_transport(t); }
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) { \ ADB_LOGE(ADB_FDEV, "... snprintf failed."); \ return; \ } \ if (max_chars < (unsigned int)printed_out) { \ ADB_LOGE(ADB_FDEV, "... snprintf out of space."); \ 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(" "); } ADB_LOGD(ADB_FDEV, "%s fd_table[]->fd = {%s}", extra_msg, msg_buff); }
static void transport_registration_func(int _fd, unsigned ev, void *data) { tmsg m; pthread_t output_thread_ptr; pthread_t input_thread_ptr; int s[2]; atransport *t; if (!(ev & FDE_READ)) { return; } if (transport_read_action(_fd, &m)) { fatal_errno("cannot read transport registration socket"); } t = m.transport; if (m.action == 0) { ADB_LOGD(ADB_TSPT, "transport: %s removing and free'ing %d", t->serial, t->transport_socket); /* IMPORTANT: the remove closes one half of the ** socket pair. The close closes the other half. */ fdevent_remove(&(t->transport_fde)); close(t->fd); pthread_mutex_lock(&transport_lock); t->next->prev = t->prev; t->prev->next = t->next; pthread_mutex_unlock(&transport_lock); run_transport_disconnects(t); if (t->product) free(t->product); if (t->serial) free(t->serial); if (t->model) free(t->model); if (t->device) free(t->device); if (t->devpath) free(t->devpath); memset(t,0xee,sizeof(atransport)); free(t); update_transports(); return; } /* don't create transport threads for inaccessible devices */ if (t->connection_state != CS_NOPERM) { /* initial references are the two threads */ t->ref_count = 2; if (adb_socketpair(s)) { fatal_errno("cannot open transport socketpair"); } ADB_LOGD(ADB_TSPT, "transport: %s socketpair: (%d,%d) starting", t->serial, s[0], s[1]); t->transport_socket = s[0]; t->fd = s[1]; fdevent_install(&(t->transport_fde), t->transport_socket, transport_socket_events, t); fdevent_set(&(t->transport_fde), FDE_READ); if (adb_thread_create(&input_thread_ptr, input_thread, t)) { fatal_errno("cannot create input thread"); } if (adb_thread_create(&output_thread_ptr, output_thread, t)) { fatal_errno("cannot create output thread"); } } pthread_mutex_lock(&transport_lock); /* remove from pending list */ t->next->prev = t->prev; t->prev->next = t->next; /* put us on the master device list */ t->next = &transport_list; t->prev = transport_list.prev; t->next->prev = t; t->prev->next = t; pthread_mutex_unlock(&transport_lock); t->disconnects.next = t->disconnects.prev = &t->disconnects; update_transports(); }
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; ADB_LOGD(ADB_FDEV, "select() returned n=%d, errno=%d", 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: ADB_LOGW(ADB_FDEV, "Unexpected select() error=%d", 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", i); } fde->events |= events; ADB_LOGD(ADB_FDEV, "got events fde->fd=%d events=%04x, state=%04x", fde->fd, fde->events, fde->state); if (fde->state & FDE_PENDING) continue; fde->state |= FDE_PENDING; fdevent_plist_enqueue(fde); } } }