/* * Copyright (C) 2010 The Android Open Source Project * * 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. */ #include "android/async-console.h" #include /* * State diagram, ommitting the ERROR state * * INITIAL -->--+ * | | * | CONNECTING * | | * |<-----+ * v * READ_BANNER_1 * | * v * READ_BANNER_2 * | * v * COMPLETE */ enum { STATE_INITIAL, STATE_CONNECTING, STATE_ERROR, STATE_READ_BANNER_1, STATE_READ_BANNER_2, STATE_COMPLETE }; /* A helper function to prepare the line reader and switch to a new state */ static AsyncStatus _acc_prepareLineReader(AsyncConsoleConnector* acc, int newState) { acc->state = newState; asyncLineReader_init(acc->lreader, acc->lbuff, sizeof(acc->lbuff), acc->io); return ASYNC_NEED_MORE; } AsyncStatus asyncConsoleConnector_connect(AsyncConsoleConnector* acc, const SockAddress* address, LoopIo* io) { acc->state = STATE_INITIAL; acc->address = address[0]; acc->io = io; return asyncConsoleConnector_run(acc); } AsyncStatus asyncConsoleConnector_run(AsyncConsoleConnector* acc) { AsyncStatus status = ASYNC_NEED_MORE; for (;;) { switch (acc->state) { case STATE_ERROR: /* reporting previous error */ errno = acc->error; return ASYNC_ERROR; case STATE_INITIAL: /* initial connection attempt */ acc->state = STATE_CONNECTING; status = asyncConnector_init(acc->connector, &acc->address, acc->io); if (status == ASYNC_ERROR) goto SET_ERROR; if (status == ASYNC_COMPLETE) { /* immediate connection */ _acc_prepareLineReader(acc, STATE_READ_BANNER_1); continue; } break; case STATE_CONNECTING: /* still trying to connect */ status = asyncConnector_run(acc->connector); if (status == ASYNC_ERROR) goto SET_ERROR; if (status == ASYNC_COMPLETE) { _acc_prepareLineReader(acc, STATE_READ_BANNER_1); continue; } break; case STATE_READ_BANNER_1: /* reading the first banner line */ status = asyncLineReader_read(acc->lreader); if (status == ASYNC_ERROR) goto SET_ERROR; if (status == ASYNC_COMPLETE) { /* Check that first line starts with "Android Console:", * otherwise we're not talking to the right program. */ const char* line = asyncLineReader_getLine(acc->lreader); if (line == NULL || memcmp(line, "Android Console:", 16)) { goto BAD_BANNER; } /* ok, fine, prepare for the next banner line then */ _acc_prepareLineReader(acc, STATE_READ_BANNER_2); continue; } break; case STATE_READ_BANNER_2: /* reading the second banner line */ status = asyncLineReader_read(acc->lreader); if (status == ASYNC_ERROR) goto SET_ERROR; if (status == ASYNC_COMPLETE) { const char* line = asyncLineReader_getLine(acc->lreader); if (line == NULL) { goto BAD_BANNER; } /* ok, we're done !*/ acc->state = STATE_COMPLETE; return ASYNC_COMPLETE; } break; case STATE_COMPLETE: status = ASYNC_COMPLETE; } return status; } BAD_BANNER: errno = ENOPROTOOPT; SET_ERROR: acc->state = STATE_ERROR; acc->error = errno; return ASYNC_ERROR; }