blob: 523c2b2f1d80f7684809be08c1af5c338e9ffac9 [file] [log] [blame]
/*
* 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;
}