| /* |
| * 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 <string.h> |
| |
| /* |
| * 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; |
| } |