static void wait_for_state(int fd, void* data) {
    std::unique_ptr<state_info> sinfo(reinterpret_cast<state_info*>(data));

    D("wait_for_state %d", sinfo->state);

    while (true) {
        bool is_ambiguous = false;
        std::string error = "unknown error";
        const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
        atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);

        if (t != nullptr && t->connection_state == sinfo->state) {
            SendOkay(fd);
            break;
        } else if (!is_ambiguous) {
            adb_sleep_ms(1000);
            // Try again...
        } else {
            SendFail(fd, error);
            break;
        }
    }

    adb_close(fd);
    D("wait_for_state is done");
}
Exemple #2
0
int adb_remove_forward(unsigned short local, transport_type ttype,
                       const char* serial) {
    char *err;
    atransport *transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
    if(!transport) {
        return 1;
    }
    char local_name[32];
    snprintf(local_name, sizeof(local_name), "tcp:%u", local);
    int r = remove_listener(local_name, transport);
    return r==0;
}
Exemple #3
0
int adb_create_forward(unsigned short local, unsigned short remote,
                       transport_type ttype, const char* serial,
                       int no_rebind) {
    char *err;
    atransport *transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
    if (!transport) {
        return 1;
    }

    char local_name[32];
    char remote_name[32];
    snprintf(local_name, sizeof(local_name), "tcp:%u", local);
    snprintf(remote_name, sizeof(remote_name), "tcp:%u", remote);
    int r = install_listener(local_name, remote_name, transport, no_rebind);
    return r==0;
}
Exemple #4
0
static void wait_for_state(int fd, void* cookie)
{
    struct state_info* sinfo = cookie;
    char* err = "unknown error";

    D("wait_for_state %d\n", sinfo->state);

    atransport *t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &err);
    if(t != 0) {
        writex(fd, "OKAY", 4);
    } else {
        sendfailmsg(fd, err);
    }

    if (sinfo->serial)
        free(sinfo->serial);
    free(sinfo);
    adb_close(fd);
    D("wait_for_state is done\n");
}
static void wait_for_state(int fd, void* cookie)
{
    state_info* sinfo = reinterpret_cast<state_info*>(cookie);

    D("wait_for_state %d\n", sinfo->state);

    std::string error_msg = "unknown error";
    atransport* t = acquire_one_transport(sinfo->state, sinfo->transport, sinfo->serial, &error_msg);
    if (t != 0) {
        SendOkay(fd);
    } else {
        SendFail(fd, error_msg);
    }

    if (sinfo->serial)
        free(sinfo->serial);
    free(sinfo);
    adb_close(fd);
    D("wait_for_state is done\n");
}
Exemple #6
0
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
{
    atransport *transport = NULL;
    char buf[4096];

    if(!strcmp(service, "kill")) {
        fprintf(stderr,"adb server killed by remote request\n");
        fflush(stdout);
        adb_write(reply_fd, "OKAY", 4);
        usb_cleanup();
        exit(0);
    }

#if ADB_HOST
    // "transport:" is used for switching transport with a specified serial number
    // "transport-usb:" is used for switching transport to the only USB transport
    // "transport-local:" is used for switching transport to the only local transport
    // "transport-any:" is used for switching transport to the only transport
    if (!strncmp(service, "transport", strlen("transport"))) {
        char* error_string = "unknown failure";
        transport_type type = kTransportAny;

        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
            type = kTransportUsb;
        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
            type = kTransportLocal;
        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
            type = kTransportAny;
        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
            service += strlen("transport:");
            serial = strdup(service);
        }

        transport = acquire_one_transport(CS_ANY, type, serial, &error_string);

        if (transport) {
            s->transport = transport;
            adb_write(reply_fd, "OKAY", 4);
        } else {
            sendfailmsg(reply_fd, error_string);
        }
        return 1;
    }

    // return a list of all connected devices
    if (!strcmp(service, "devices")) {
        char buffer[4096];
        memset(buf, 0, sizeof(buf));
        memset(buffer, 0, sizeof(buffer));
        D("Getting device list \n");
        list_transports(buffer, sizeof(buffer));
        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
        D("Wrote device list \n");
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }

    // add a new TCP transport, device or emulator
    if (!strncmp(service, "connect:", 8)) {
        char buffer[4096];
        char* host = service + 8;
        if (!strncmp(host, "emu:", 4)) {
            connect_emulator(host + 4, buffer, sizeof(buffer));
        } else {
            connect_device(host, buffer, sizeof(buffer));
        }
        // Send response for emulator and device
        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }

    // remove TCP transport
    if (!strncmp(service, "disconnect:", 11)) {
        char buffer[4096];
        memset(buffer, 0, sizeof(buffer));
        char* serial = service + 11;
        if (serial[0] == 0) {
            // disconnect from all TCP devices
            unregister_all_tcp_transports();
        } else {
            char hostbuf[100];
            // assume port 5555 if no port is specified
            if (!strchr(serial, ':')) {
                snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
                serial = hostbuf;
            }
            atransport *t = find_transport(serial);

            if (t) {
                unregister_transport(t);
            } else {
                snprintf(buffer, sizeof(buffer), "No such device %s", serial);
            }
        }

        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }

    // returns our value for ADB_SERVER_VERSION
    if (!strcmp(service, "version")) {
        char version[12];
        snprintf(version, sizeof version, "%04x", ADB_SERVER_VERSION);
        snprintf(buf, sizeof buf, "OKAY%04x%s", (unsigned)strlen(version), version);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }

    if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
        char *out = "unknown";
         transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
       if (transport && transport->serial) {
            out = transport->serial;
        }
        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }
    // indicates a new emulator instance has started
    if (!strncmp(service,"emulator:",9)) {
        int  port = atoi(service+9);
        local_connect(port);
        /* we don't even need to send a reply */
        return 0;
    }
#endif // ADB_HOST

    if(!strncmp(service,"forward:",8) || !strncmp(service,"killforward:",12)) {
        char *local, *remote, *err;
        int r;
        atransport *transport;

        int createForward = strncmp(service,"kill",4);

        local = service + (createForward ? 8 : 12);
        remote = strchr(local,';');
        if(remote == 0) {
            sendfailmsg(reply_fd, "malformed forward spec");
            return 0;
        }

        *remote++ = 0;
        if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){
            sendfailmsg(reply_fd, "malformed forward spec");
            return 0;
        }

        transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
        if (!transport) {
            sendfailmsg(reply_fd, err);
            return 0;
        }

        if (createForward) {
            r = install_listener(local, remote, transport);
        } else {
            r = remove_listener(local, remote, transport);
        }
        if(r == 0) {
                /* 1st OKAY is connect, 2nd OKAY is status */
            writex(reply_fd, "OKAYOKAY", 8);
            return 0;
        }

        if (createForward) {
            sendfailmsg(reply_fd, (r == -1) ? "cannot rebind smartsocket" : "cannot bind socket");
        } else {
            sendfailmsg(reply_fd, "cannot remove listener");
        }
        return 0;
    }

    if(!strncmp(service,"get-state",strlen("get-state"))) {
        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
        char *state = connection_state_name(transport);
        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }
    return -1;
}
Exemple #7
0
static int smart_socket_enqueue(asocket* s, apacket* p) {
    unsigned len;
#if ADB_HOST
    char* service = nullptr;
    char* serial = nullptr;
    TransportId transport_id = 0;
    TransportType type = kTransportAny;
#endif

    D("SS(%d): enqueue %zu", s->id, p->len);

    if (s->pkt_first == 0) {
        s->pkt_first = p;
        s->pkt_last = p;
    } else {
        if ((s->pkt_first->len + p->len) > s->get_max_payload()) {
            D("SS(%d): overflow", s->id);
            put_apacket(p);
            goto fail;
        }

        memcpy(s->pkt_first->data + s->pkt_first->len, p->data, p->len);
        s->pkt_first->len += p->len;
        put_apacket(p);

        p = s->pkt_first;
    }

    /* don't bother if we can't decode the length */
    if (p->len < 4) {
        return 0;
    }

    len = unhex(p->data, 4);
    if ((len < 1) || (len > MAX_PAYLOAD)) {
        D("SS(%d): bad size (%d)", s->id, len);
        goto fail;
    }

    D("SS(%d): len is %d", s->id, len);
    /* can't do anything until we have the full header */
    if ((len + 4) > p->len) {
        D("SS(%d): waiting for %zu more bytes", s->id, len + 4 - p->len);
        return 0;
    }

    p->data[len + 4] = 0;

    D("SS(%d): '%s'", s->id, (char*)(p->data + 4));

#if ADB_HOST
    service = (char*)p->data + 4;
    if (!strncmp(service, "host-serial:", strlen("host-serial:"))) {
        char* serial_end;
        service += strlen("host-serial:");

        // serial number should follow "host:" and could be a host:port string.
        serial_end = internal::skip_host_serial(service);
        if (serial_end) {
            *serial_end = 0;  // terminate string
            serial = service;
            service = serial_end + 1;
        }
    } else if (!strncmp(service, "host-transport-id:", strlen("host-transport-id:"))) {
        service += strlen("host-transport-id:");
        transport_id = strtoll(service, &service, 10);

        if (*service != ':') {
            return -1;
        }
        service++;
    } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
        type = kTransportUsb;
        service += strlen("host-usb:");
    } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
        type = kTransportLocal;
        service += strlen("host-local:");
    } else if (!strncmp(service, "host:", strlen("host:"))) {
        type = kTransportAny;
        service += strlen("host:");
    } else {
        service = nullptr;
    }

    if (service) {
        asocket* s2;

        /* some requests are handled immediately -- in that
        ** case the handle_host_request() routine has sent
        ** the OKAY or FAIL message and all we have to do
        ** is clean up.
        */
        if (handle_host_request(service, type, serial, transport_id, s->peer->fd, s) == 0) {
            /* XXX fail message? */
            D("SS(%d): handled host service '%s'", s->id, service);
            goto fail;
        }
        if (!strncmp(service, "transport", strlen("transport"))) {
            D("SS(%d): okay transport", s->id);
            p->len = 0;
            return 0;
        }

        /* try to find a local service with this name.
        ** if no such service exists, we'll fail out
        ** and tear down here.
        */
        s2 = create_host_service_socket(service, serial, transport_id);
        if (s2 == 0) {
            D("SS(%d): couldn't create host service '%s'", s->id, service);
            SendFail(s->peer->fd, "unknown host service");
            goto fail;
        }

        /* we've connected to a local host service,
        ** so we make our peer back into a regular
        ** local socket and bind it to the new local
        ** service socket, acknowledge the successful
        ** connection, and close this smart socket now
        ** that its work is done.
        */
        SendOkay(s->peer->fd);

        s->peer->ready = local_socket_ready;
        s->peer->shutdown = nullptr;
        s->peer->close = local_socket_close;
        s->peer->peer = s2;
        s2->peer = s->peer;
        s->peer = 0;
        D("SS(%d): okay", s->id);
        s->close(s);

        /* initial state is "ready" */
        s2->ready(s2);
        return 0;
    }
#else /* !ADB_HOST */
    if (s->transport == nullptr) {
        std::string error_msg = "unknown failure";
        s->transport = acquire_one_transport(kTransportAny, nullptr, 0, nullptr, &error_msg);
        if (s->transport == nullptr) {
            SendFail(s->peer->fd, error_msg);
            goto fail;
        }
    }
#endif

    if (!s->transport) {
        SendFail(s->peer->fd, "device offline (no transport)");
        goto fail;
    } else if (s->transport->GetConnectionState() == kCsOffline) {
        /* if there's no remote we fail the connection
         ** right here and terminate it
         */
        SendFail(s->peer->fd, "device offline (transport offline)");
        goto fail;
    }

    /* instrument our peer to pass the success or fail
    ** message back once it connects or closes, then
    ** detach from it, request the connection, and
    ** tear down
    */
    s->peer->ready = local_socket_ready_notify;
    s->peer->shutdown = nullptr;
    s->peer->close = local_socket_close_notify;
    s->peer->peer = 0;
    /* give him our transport and upref it */
    s->peer->transport = s->transport;

    connect_to_remote(s->peer, (char*)(p->data + 4));
    s->peer = 0;
    s->close(s);
    return 1;

fail:
    /* we're going to close our peer as a side-effect, so
    ** return -1 to signal that state to the local socket
    ** who is enqueueing against us
    */
    s->close(s);
    return -1;
}
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
{
    if(!strcmp(service, "kill")) {
        fprintf(stderr,"adb server killed by remote request\n");
        fflush(stdout);
        SendOkay(reply_fd);
        usb_cleanup();
        exit(0);
    }

#if ADB_HOST
    atransport *transport = NULL;
    // "transport:" is used for switching transport with a specified serial number
    // "transport-usb:" is used for switching transport to the only USB transport
    // "transport-local:" is used for switching transport to the only local transport
    // "transport-any:" is used for switching transport to the only transport
    if (!strncmp(service, "transport", strlen("transport"))) {
        transport_type type = kTransportAny;

        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
            type = kTransportUsb;
        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
            type = kTransportLocal;
        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
            type = kTransportAny;
        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
            service += strlen("transport:");
            serial = service;
        }

        std::string error_msg = "unknown failure";
        transport = acquire_one_transport(CS_ANY, type, serial, &error_msg);

        if (transport) {
            s->transport = transport;
            SendOkay(reply_fd);
        } else {
            SendFail(reply_fd, error_msg);
        }
        return 1;
    }

    // return a list of all connected devices
    if (!strncmp(service, "devices", 7)) {
        bool long_listing = (strcmp(service+7, "-l") == 0);
        if (long_listing || service[7] == 0) {
            D("Getting device list...\n");
            std::string device_list = list_transports(long_listing);
            D("Sending device list...\n");
            SendOkay(reply_fd);
            SendProtocolString(reply_fd, device_list);
            return 0;
        }
        return 1;
    }

    // remove TCP transport
    if (!strncmp(service, "disconnect:", 11)) {
        char buffer[4096];
        memset(buffer, 0, sizeof(buffer));
        char* serial = service + 11;
        if (serial[0] == 0) {
            // disconnect from all TCP devices
            unregister_all_tcp_transports();
        } else {
            char hostbuf[100];
            // assume port 5555 if no port is specified
            if (!strchr(serial, ':')) {
                snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
                serial = hostbuf;
            }
            atransport *t = find_transport(serial);

            if (t) {
                unregister_transport(t);
            } else {
                snprintf(buffer, sizeof(buffer), "No such device %s", serial);
            }
        }

        SendOkay(reply_fd);
        SendProtocolString(reply_fd, buffer);
        return 0;
    }

    // returns our value for ADB_SERVER_VERSION
    if (!strcmp(service, "version")) {
        SendOkay(reply_fd);
        SendProtocolString(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
        return 0;
    }

    if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
        const char *out = "unknown";
        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
        if (transport && transport->serial) {
            out = transport->serial;
        }
        SendOkay(reply_fd);
        SendProtocolString(reply_fd, out);
        return 0;
    }
    if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
        const char *out = "unknown";
        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
        if (transport && transport->devpath) {
            out = transport->devpath;
        }
        SendOkay(reply_fd);
        SendProtocolString(reply_fd, out);
        return 0;
    }
    // indicates a new emulator instance has started
    if (!strncmp(service,"emulator:",9)) {
        int  port = atoi(service+9);
        local_connect(port);
        /* we don't even need to send a reply */
        return 0;
    }

    if(!strncmp(service,"get-state",strlen("get-state"))) {
        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
        SendOkay(reply_fd);
        SendProtocolString(reply_fd, transport ? transport->connection_state_name() : "unknown");
        return 0;
    }
#endif // ADB_HOST

    int ret = handle_forward_request(service, ttype, serial, reply_fd);
    if (ret >= 0)
      return ret - 1;
    return -1;
}
// Try to handle a network forwarding request.
// This returns 1 on success, 0 on failure, and -1 to indicate this is not
// a forwarding-related request.
int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd)
{
    if (!strcmp(service, "list-forward")) {
        // Create the list of forward redirections.
        std::string listeners = format_listeners();
#if ADB_HOST
        SendOkay(reply_fd);
#endif
        SendProtocolString(reply_fd, listeners);
        return 1;
    }

    if (!strcmp(service, "killforward-all")) {
        remove_all_listeners();
#if ADB_HOST
        /* On the host: 1st OKAY is connect, 2nd OKAY is status */
        SendOkay(reply_fd);
#endif
        SendOkay(reply_fd);
        return 1;
    }

    if (!strncmp(service, "forward:",8) ||
        !strncmp(service, "killforward:",12)) {
        char *local, *remote;
        atransport *transport;

        int createForward = strncmp(service, "kill", 4);
        int no_rebind = 0;

        local = strchr(service, ':') + 1;

        // Handle forward:norebind:<local>... here
        if (createForward && !strncmp(local, "norebind:", 9)) {
            no_rebind = 1;
            local = strchr(local, ':') + 1;
        }

        remote = strchr(local,';');

        if (createForward) {
            // Check forward: parameter format: '<local>;<remote>'
            if(remote == 0) {
                SendFail(reply_fd, "malformed forward spec");
                return 1;
            }

            *remote++ = 0;
            if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) {
                SendFail(reply_fd, "malformed forward spec");
                return 1;
            }
        } else {
            // Check killforward: parameter format: '<local>'
            if (local[0] == 0) {
                SendFail(reply_fd, "malformed forward spec");
                return 1;
            }
        }

        std::string error_msg;
        transport = acquire_one_transport(CS_ANY, ttype, serial, &error_msg);
        if (!transport) {
            SendFail(reply_fd, error_msg);
            return 1;
        }

        install_status_t r;
        if (createForward) {
            r = install_listener(local, remote, transport, no_rebind);
        } else {
            r = remove_listener(local, transport);
        }
        if (r == INSTALL_STATUS_OK) {
#if ADB_HOST
            /* On the host: 1st OKAY is connect, 2nd OKAY is status */
            SendOkay(reply_fd);
#endif
            SendOkay(reply_fd);
            return 1;
        }

        std::string message;
        switch (r) {
          case INSTALL_STATUS_OK: message = " "; break;
          case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
          case INSTALL_STATUS_CANNOT_BIND:
            message = android::base::StringPrintf("cannot bind to socket: %s", strerror(errno));
            break;
          case INSTALL_STATUS_CANNOT_REBIND:
            message = android::base::StringPrintf("cannot rebind existing socket: %s", strerror(errno));
            break;
          case INSTALL_STATUS_LISTENER_NOT_FOUND: message = "listener not found"; break;
        }
        SendFail(reply_fd, message);
        return 1;
    }
    return 0;
}
Exemple #10
0
int handle_host_request(char *service, transport_type ttype, char* serial, int reply_fd, asocket *s)
{
    D("service=%s, ttype=%d, serial=%s, reply_fd=%d, asocket=?\n",
        service, ttype, serial, reply_fd);
    atransport *transport = NULL;
    char buf[4096];

    if(!strcmp(service, "kill")) {
        fprintf(stderr,"adb server killed by remote request\n");
        fflush(stdout);
        adb_write(reply_fd, "OKAY", 4);
        usb_cleanup();
        exit(0);
    }

#if ADB_HOST
    // "transport:" is used for switching transport with a specified serial number
    // "transport-usb:" is used for switching transport to the only USB transport
    // "transport-local:" is used for switching transport to the only local transport
    // "transport-any:" is used for switching transport to the only transport
    if (!strncmp(service, "transport", strlen("transport"))) {
        char* error_string = "unknown failure";
        transport_type type = kTransportAny;

        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
            type = kTransportUsb;
        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
            type = kTransportLocal;
        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
            type = kTransportAny;
        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
            service += strlen("transport:");
            serial = service;
        }

        transport = acquire_one_transport(CS_ANY, type, serial, &error_string);

        if (transport) {
            s->transport = transport;
            adb_write(reply_fd, "OKAY", 4);
        } else {
            sendfailmsg(reply_fd, error_string);
        }
        return 1;
    }

    // return a list of all connected devices
    if (!strncmp(service, "devices", 7)) {
        char buffer[4096];
        int use_long = !strcmp(service+7, "-l");
        if (use_long || service[7] == 0) {
            memset(buf, 0, sizeof(buf));
            memset(buffer, 0, sizeof(buffer));
            D("Getting device list \n");
            list_transports(buffer, sizeof(buffer), use_long);
            snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
            D("Wrote device list \n");
            writex(reply_fd, buf, strlen(buf));
            return 0;
        }
    }

    // add a new TCP transport, device or emulator
    if (!strncmp(service, "connect:", 8)) {
        char buffer[4096];
        char* host = service + 8;
        if (!strncmp(host, "emu:", 4)) {
            connect_emulator(host + 4, buffer, sizeof(buffer));
        } else {
            connect_device(host, buffer, sizeof(buffer));
        }
        // Send response for emulator and device
        snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer), buffer);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }

    // remove TCP transport
	// Disable the disconnect service to prevent complexity with Windows
  //
    // returns our value for ADB_SERVER_VERSION
    if (!strcmp(service, "version")) {
        printf("the version service is disabled.\n");
        return -1;
    }

    if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
        char *out = "unknown";
         transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
       if (transport && transport->serial) {
            out = transport->serial;
        }
        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }
    if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
        char *out = "unknown";
         transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
       if (transport && transport->devpath) {
            out = transport->devpath;
        }
        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(out),out);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }
    // indicates a new emulator instance has started
    if (!strncmp(service,"emulator:",9)) {
        int  port = atoi(service+9);
        local_connect(port);
        /* we don't even need to send a reply */
        return 0;
    }
#endif // ADB_HOST

    if(!strcmp(service,"list-forward")) {
        // Create the list of forward redirections.
        char header[9];
        int buffer_size = format_listeners(NULL, 0);
        // Add one byte for the trailing zero.
        char* buffer = (char*)malloc(buffer_size+1);
        (void) format_listeners(buffer, buffer_size+1);
        snprintf(header, sizeof header, "OKAY%04x", buffer_size);
        writex(reply_fd, header, 8);
        writex(reply_fd, buffer, buffer_size);
        free(buffer);
        return 0;
    }

    if (!strcmp(service,"killforward-all")) {
        remove_all_listeners();
        adb_write(reply_fd, "OKAYOKAY", 8);
        return 0;
    }

    if(!strncmp(service,"forward:",8) ||
       !strncmp(service,"killforward:",12)) {
        char *local, *remote, *err;
        int r;
        atransport *transport;

        int createForward = strncmp(service,"kill",4);
        int no_rebind = 0;

        local = strchr(service, ':') + 1;

        // Handle forward:norebind:<local>... here
        if (createForward && !strncmp(local, "norebind:", 9)) {
            no_rebind = 1;
            local = strchr(local, ':') + 1;
        }

        remote = strchr(local,';');

        if (createForward) {
            // Check forward: parameter format: '<local>;<remote>'
            if(remote == 0) {
                sendfailmsg(reply_fd, "malformed forward spec");
                return 0;
            }

            *remote++ = 0;
            if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')){
                sendfailmsg(reply_fd, "malformed forward spec");
                return 0;
            }
        } else {
            // Check killforward: parameter format: '<local>'
            if (local[0] == 0) {
                sendfailmsg(reply_fd, "malformed forward spec");
                return 0;
            }
        }

        transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
        if (!transport) {
            sendfailmsg(reply_fd, err);
            return 0;
        }

        if (createForward) {
            r = install_listener(local, remote, transport, no_rebind);
        } else {
            r = remove_listener(local, transport);
        }
        if(r == 0) {
                /* 1st OKAY is connect, 2nd OKAY is status */
            writex(reply_fd, "OKAYOKAY", 8);
            return 0;
        }

        if (createForward) {
            const char* message;
            switch (r) {
              case INSTALL_STATUS_CANNOT_BIND:
                message = "cannot bind to socket";
                break;
              case INSTALL_STATUS_CANNOT_REBIND:
                message = "cannot rebind existing socket";
                break;
              default:
                message = "internal error";
            }
            sendfailmsg(reply_fd, message);
        } else {
            sendfailmsg(reply_fd, "cannot remove listener");
        }
        return 0;
    }

    if(!strncmp(service,"get-state",strlen("get-state"))) {
        transport = acquire_one_transport(CS_ANY, ttype, serial, NULL);
        char *state = connection_state_name(transport);
        snprintf(buf, sizeof buf, "OKAY%04x%s",(unsigned)strlen(state),state);
        writex(reply_fd, buf, strlen(buf));
        return 0;
    }
    return -1;
}
Exemple #11
0
// Try to handle a network forwarding request.
// This returns 1 on success, 0 on failure, and -1 to indicate this is not
// a forwarding-related request.
int handle_forward_request(const char* service, transport_type ttype, char* serial, int reply_fd)
{
    if (!strcmp(service, "list-forward")) {
        // Create the list of forward redirections.
        int buffer_size = format_listeners(NULL, 0);
        // Add one byte for the trailing zero.
        char* buffer = malloc(buffer_size + 1);
        if (buffer == NULL) {
            sendfailmsg(reply_fd, "not enough memory");
            return 1;
        }
        (void) format_listeners(buffer, buffer_size + 1);
#if ADB_HOST
        send_msg_with_okay(reply_fd, buffer, buffer_size);
#else
        send_msg_with_header(reply_fd, buffer, buffer_size);
#endif
        free(buffer);
        return 1;
    }

    if (!strcmp(service, "killforward-all")) {
        remove_all_listeners();
#if ADB_HOST
        /* On the host: 1st OKAY is connect, 2nd OKAY is status */
        adb_write(reply_fd, "OKAY", 4);
#endif
        adb_write(reply_fd, "OKAY", 4);
        return 1;
    }

    if (!strncmp(service, "forward:",8) ||
        !strncmp(service, "killforward:",12)) {
        char *local, *remote, *err;
        int r;
        atransport *transport;

        int createForward = strncmp(service, "kill", 4);
        int no_rebind = 0;

        local = strchr(service, ':') + 1;

        // Handle forward:norebind:<local>... here
        if (createForward && !strncmp(local, "norebind:", 9)) {
            no_rebind = 1;
            local = strchr(local, ':') + 1;
        }

        remote = strchr(local,';');

        if (createForward) {
            // Check forward: parameter format: '<local>;<remote>'
            if(remote == 0) {
                sendfailmsg(reply_fd, "malformed forward spec");
                return 1;
            }

            *remote++ = 0;
            if((local[0] == 0) || (remote[0] == 0) || (remote[0] == '*')) {
                sendfailmsg(reply_fd, "malformed forward spec");
                return 1;
            }
        } else {
            // Check killforward: parameter format: '<local>'
            if (local[0] == 0) {
                sendfailmsg(reply_fd, "malformed forward spec");
                return 1;
            }
        }

        transport = acquire_one_transport(CS_ANY, ttype, serial, &err);
        if (!transport) {
            sendfailmsg(reply_fd, err);
            return 1;
        }

        if (createForward) {
            r = install_listener(local, remote, transport, no_rebind);
        } else {
            r = remove_listener(local, transport);
        }
        if(r == 0) {
#if ADB_HOST
            /* On the host: 1st OKAY is connect, 2nd OKAY is status */
            writex(reply_fd, "OKAY", 4);
#endif
            writex(reply_fd, "OKAY", 4);
            return 1;
        }

        if (createForward) {
            const char* message;
            switch (r) {
              case INSTALL_STATUS_CANNOT_BIND:
                message = "cannot bind to socket";
                break;
              case INSTALL_STATUS_CANNOT_REBIND:
                message = "cannot rebind existing socket";
                break;
              default:
                message = "internal error";
            }
            sendfailmsg(reply_fd, message);
        } else {
            sendfailmsg(reply_fd, "cannot remove listener");
        }
        return 1;
    }
    return 0;
}
Exemple #12
0
// Try to handle a network forwarding request.
// This returns 1 on success, 0 on failure, and -1 to indicate this is not
// a forwarding-related request.
int handle_forward_request(const char* service, TransportType type, const char* serial, int reply_fd)
{
    if (!strcmp(service, "list-forward")) {
        // Create the list of forward redirections.
        std::string listeners = format_listeners();
#if ADB_HOST
        SendOkay(reply_fd);
#endif
        return SendProtocolString(reply_fd, listeners);
    }

    if (!strcmp(service, "killforward-all")) {
        remove_all_listeners();
#if ADB_HOST
        /* On the host: 1st OKAY is connect, 2nd OKAY is status */
        SendOkay(reply_fd);
#endif
        SendOkay(reply_fd);
        return 1;
    }

    if (!strncmp(service, "forward:", 8) || !strncmp(service, "killforward:", 12)) {
        // killforward:local
        // forward:(norebind:)?local;remote
        bool kill_forward = false;
        bool no_rebind = false;
        if (android::base::StartsWith(service, "killforward:")) {
            kill_forward = true;
            service += 12;
        } else {
            service += 8;   // skip past "forward:"
            if (android::base::StartsWith(service, "norebind:")) {
                no_rebind = true;
                service += 9;
            }
        }

        std::vector<std::string> pieces = android::base::Split(service, ";");

        if (kill_forward) {
            // Check killforward: parameter format: '<local>'
            if (pieces.size() != 1 || pieces[0].empty()) {
                SendFail(reply_fd, android::base::StringPrintf("bad killforward: %s", service));
                return 1;
            }
        } else {
            // Check forward: parameter format: '<local>;<remote>'
            if (pieces.size() != 2 || pieces[0].empty() || pieces[1].empty() || pieces[1][0] == '*') {
                SendFail(reply_fd, android::base::StringPrintf("bad forward: %s", service));
                return 1;
            }
        }

        std::string error_msg;
        atransport* transport = acquire_one_transport(type, serial, nullptr, &error_msg);
        if (!transport) {
            SendFail(reply_fd, error_msg);
            return 1;
        }

        std::string error;
        InstallStatus r;
        int resolved_tcp_port = 0;
        if (kill_forward) {
            r = remove_listener(pieces[0].c_str(), transport);
        } else {
            r = install_listener(pieces[0], pieces[1].c_str(), transport, no_rebind,
                                 &resolved_tcp_port, &error);
        }
        if (r == INSTALL_STATUS_OK) {
#if ADB_HOST
            // On the host: 1st OKAY is connect, 2nd OKAY is status.
            SendOkay(reply_fd);
#endif
            SendOkay(reply_fd);

            // If a TCP port was resolved, send the actual port number back.
            if (resolved_tcp_port != 0) {
                SendProtocolString(reply_fd, android::base::StringPrintf("%d", resolved_tcp_port));
            }

            return 1;
        }

        std::string message;
        switch (r) {
          case INSTALL_STATUS_OK: message = "success (!)"; break;
          case INSTALL_STATUS_INTERNAL_ERROR: message = "internal error"; break;
          case INSTALL_STATUS_CANNOT_BIND:
            message = android::base::StringPrintf("cannot bind listener: %s",
                                                  error.c_str());
            break;
          case INSTALL_STATUS_CANNOT_REBIND:
            message = android::base::StringPrintf("cannot rebind existing socket");
            break;
          case INSTALL_STATUS_LISTENER_NOT_FOUND:
            message = android::base::StringPrintf("listener '%s' not found", service);
            break;
        }
        SendFail(reply_fd, message);
        return 1;
    }
    return 0;
}
Exemple #13
0
int handle_host_request(const char* service, TransportType type,
                        const char* serial, int reply_fd, asocket* s) {
    if (strcmp(service, "kill") == 0) {
        fprintf(stderr, "adb server killed by remote request\n");
        fflush(stdout);
        SendOkay(reply_fd);

        // On Windows, if the process exits with open sockets that
        // shutdown(SD_SEND) has not been called on, TCP RST segments will be
        // sent to the peers which will cause their next recv() to error-out
        // with WSAECONNRESET. In the case of this code, that means the client
        // may not read the OKAY sent above.
        adb_shutdown(reply_fd);

        exit(0);
    }

#if ADB_HOST
    // "transport:" is used for switching transport with a specified serial number
    // "transport-usb:" is used for switching transport to the only USB transport
    // "transport-local:" is used for switching transport to the only local transport
    // "transport-any:" is used for switching transport to the only transport
    if (!strncmp(service, "transport", strlen("transport"))) {
        TransportType type = kTransportAny;

        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
            type = kTransportUsb;
        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
            type = kTransportLocal;
        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
            type = kTransportAny;
        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
            service += strlen("transport:");
            serial = service;
        }

        std::string error;
        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
        if (t != nullptr) {
            s->transport = t;
            SendOkay(reply_fd);
        } else {
            SendFail(reply_fd, error);
        }
        return 1;
    }

    // return a list of all connected devices
    if (!strncmp(service, "devices", 7)) {
        bool long_listing = (strcmp(service+7, "-l") == 0);
        if (long_listing || service[7] == 0) {
            D("Getting device list...");
            std::string device_list = list_transports(long_listing);
            D("Sending device list...");
            return SendOkay(reply_fd, device_list);
        }
        return 1;
    }

    if (!strcmp(service, "features")) {
        std::string error;
        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
        if (t != nullptr) {
            SendOkay(reply_fd, FeatureSetToString(t->features()));
        } else {
            SendFail(reply_fd, error);
        }
        return 0;
    }

    // remove TCP transport
    if (!strncmp(service, "disconnect:", 11)) {
        const std::string address(service + 11);
        if (address.empty()) {
            kick_all_tcp_devices();
            return SendOkay(reply_fd, "disconnected everything");
        }

        std::string serial;
        std::string host;
        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
        std::string error;
        if (!android::base::ParseNetAddress(address, &host, &port, &serial, &error)) {
            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
                                                                  address.c_str(), error.c_str()));
        }
        atransport* t = find_transport(serial.c_str());
        if (t == nullptr) {
            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
                                                                  serial.c_str()));
        }
        kick_transport(t);
        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
    }

    // Returns our value for ADB_SERVER_VERSION.
    if (!strcmp(service, "version")) {
        return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
    }

    // These always report "unknown" rather than the actual error, for scripts.
    if (!strcmp(service, "get-serialno")) {
        std::string error;
        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
        if (t) {
            return SendOkay(reply_fd, t->serial ? t->serial : "unknown");
        } else {
            return SendFail(reply_fd, error);
        }
    }
    if (!strcmp(service, "get-devpath")) {
        std::string error;
        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
        if (t) {
            return SendOkay(reply_fd, t->devpath ? t->devpath : "unknown");
        } else {
            return SendFail(reply_fd, error);
        }
    }
    if (!strcmp(service, "get-state")) {
        std::string error;
        atransport* t = acquire_one_transport(type, serial, nullptr, &error);
        if (t) {
            return SendOkay(reply_fd, t->connection_state_name());
        } else {
            return SendFail(reply_fd, error);
        }
    }

    // Indicates a new emulator instance has started.
    if (!strncmp(service, "emulator:", 9)) {
        int  port = atoi(service+9);
        local_connect(port);
        /* we don't even need to send a reply */
        return 0;
    }

    if (!strcmp(service, "reconnect")) {
        if (s->transport != nullptr) {
            kick_transport(s->transport);
        }
        return SendOkay(reply_fd, "done");
    }
#endif // ADB_HOST

    int ret = handle_forward_request(service, type, serial, reply_fd);
    if (ret >= 0)
      return ret - 1;
    return -1;
}
int handle_host_request(const char* service, TransportType type,
                        const char* serial, int reply_fd, asocket* s) {
    if (strcmp(service, "kill") == 0) {
        fprintf(stderr, "adb server killed by remote request\n");
        fflush(stdout);
        SendOkay(reply_fd);
        exit(0);
    }

#if ADB_HOST
    // "transport:" is used for switching transport with a specified serial number
    // "transport-usb:" is used for switching transport to the only USB transport
    // "transport-local:" is used for switching transport to the only local transport
    // "transport-any:" is used for switching transport to the only transport
    if (!strncmp(service, "transport", strlen("transport"))) {
        TransportType type = kTransportAny;

        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
            type = kTransportUsb;
        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
            type = kTransportLocal;
        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
            type = kTransportAny;
        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
            service += strlen("transport:");
            serial = service;
        }

        std::string error_msg;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
        if (t != nullptr) {
            s->transport = t;
            SendOkay(reply_fd);
        } else {
            SendFail(reply_fd, error_msg);
        }
        return 1;
    }

    // return a list of all connected devices
    if (!strncmp(service, "devices", 7)) {
        bool long_listing = (strcmp(service+7, "-l") == 0);
        if (long_listing || service[7] == 0) {
            D("Getting device list...\n");
            std::string device_list = list_transports(long_listing);
            D("Sending device list...\n");
            return SendOkay(reply_fd, device_list);
        }
        return 1;
    }

    // remove TCP transport
    if (!strncmp(service, "disconnect:", 11)) {
        const std::string address(service + 11);
        if (address.empty()) {
            // disconnect from all TCP devices
            unregister_all_tcp_transports();
            return SendOkay(reply_fd, "disconnected everything");
        }

        std::string serial;
        std::string host;
        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
        std::string error;
        if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
                            address.c_str(), error.c_str()));
        }
        atransport* t = find_transport(serial.c_str());
        if (t == nullptr) {
            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
                            serial.c_str()));
        }
        unregister_transport(t);
        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
    }

    // returns our value for ADB_SERVER_VERSION
    if (!strcmp(service, "version")) {
        return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
    }

    // These always report "unknown" rather than the actual error, for scripts.
    if (!strcmp(service, "get-serialno")) {
        std::string ignored;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
        return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
    }
    if (!strcmp(service, "get-devpath")) {
        std::string ignored;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
        return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
    }
    if (!strcmp(service, "get-state")) {
        std::string ignored;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
        return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
    }

    // indicates a new emulator instance has started
    if (!strncmp(service, "emulator:", 9)) {
        int  port = atoi(service+9);
        local_connect(port);
        /* we don't even need to send a reply */
        return 0;
    }
#endif // ADB_HOST

    int ret = handle_forward_request(service, type, serial, reply_fd);
    if (ret >= 0)
        return ret - 1;
    return -1;
}
int handle_host_request(const char* service, TransportType type,
                        const char* serial, int reply_fd, asocket* s) {
    if (strcmp(service, "kill") == 0) {
        fprintf(stderr, "adb server killed by remote request\n");
        fflush(stdout);
        SendOkay(reply_fd);

        // At least on Windows, if we exit() without shutdown(SD_SEND) or
        // closesocket(), the client's next recv() will error-out with
        // WSAECONNRESET and they'll never read the OKAY.
        adb_shutdown(reply_fd);

        exit(0);
    }

#if ADB_HOST
    // "transport:" is used for switching transport with a specified serial number
    // "transport-usb:" is used for switching transport to the only USB transport
    // "transport-local:" is used for switching transport to the only local transport
    // "transport-any:" is used for switching transport to the only transport
    if (!strncmp(service, "transport", strlen("transport"))) {
        TransportType type = kTransportAny;

        if (!strncmp(service, "transport-usb", strlen("transport-usb"))) {
            type = kTransportUsb;
        } else if (!strncmp(service, "transport-local", strlen("transport-local"))) {
            type = kTransportLocal;
        } else if (!strncmp(service, "transport-any", strlen("transport-any"))) {
            type = kTransportAny;
        } else if (!strncmp(service, "transport:", strlen("transport:"))) {
            service += strlen("transport:");
            serial = service;
        }

        std::string error_msg;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
        if (t != nullptr) {
            s->transport = t;
            SendOkay(reply_fd);
        } else {
            SendFail(reply_fd, error_msg);
        }
        return 1;
    }

    // return a list of all connected devices
    if (!strncmp(service, "devices", 7)) {
        bool long_listing = (strcmp(service+7, "-l") == 0);
        if (long_listing || service[7] == 0) {
            D("Getting device list...");
            std::string device_list = list_transports(long_listing);
            D("Sending device list...");
            return SendOkay(reply_fd, device_list);
        }
        return 1;
    }

    if (!strcmp(service, "features")) {
        std::string error_msg;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
        if (t != nullptr) {
            SendOkay(reply_fd, android::base::Join(t->features(), '\n'));
        } else {
            SendFail(reply_fd, error_msg);
        }
        return 0;
    }

    if (!strncmp(service, "check-feature:", strlen("check-feature:"))) {
        std::string error_msg;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
        if (t && t->CanUseFeature(service + strlen("check-feature:"))) {
            // We could potentially extend this to reply with the feature
            // version if that becomes necessary.
            SendOkay(reply_fd, "1");
        } else {
            // Empty response means unsupported feature.
            SendOkay(reply_fd, "");
        }
        return 0;
    }

    // remove TCP transport
    if (!strncmp(service, "disconnect:", 11)) {
        const std::string address(service + 11);
        if (address.empty()) {
            kick_all_tcp_devices();
            return SendOkay(reply_fd, "disconnected everything");
        }

        std::string serial;
        std::string host;
        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
        std::string error;
        if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
                                                                  address.c_str(), error.c_str()));
        }
        atransport* t = find_transport(serial.c_str());
        if (t == nullptr) {
            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
                                                                  serial.c_str()));
        }
        kick_transport(t);
        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
    }

    // returns our value for ADB_SERVER_VERSION
    if (!strcmp(service, "version")) {
        return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
    }

    // These always report "unknown" rather than the actual error, for scripts.
    if (!strcmp(service, "get-serialno")) {
        std::string ignored;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
        return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
    }
    if (!strcmp(service, "get-devpath")) {
        std::string ignored;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
        return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
    }
    if (!strcmp(service, "get-state")) {
        std::string ignored;
        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
        return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
    }

    // indicates a new emulator instance has started
    if (!strncmp(service, "emulator:", 9)) {
        int  port = atoi(service+9);
        local_connect(port);
        /* we don't even need to send a reply */
        return 0;
    }
#endif // ADB_HOST

    int ret = handle_forward_request(service, type, serial, reply_fd);
    if (ret >= 0)
      return ret - 1;
    return -1;
}