aboutsummaryrefslogtreecommitdiffstats
path: root/android/async-console.c
blob: f486df05a047b5ab5f66eadd3d280e38c61021bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
 * 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, LoopIo* io, int newState)
{
    acc->state = newState;
    asyncLineReader_init(acc->lreader, acc->lbuff, sizeof(acc->lbuff), io);
    return ASYNC_NEED_MORE;
}

AsyncStatus
asyncConsoleConnector_connect(AsyncConsoleConnector* acc,
                              const SockAddress*     address,
                              LoopIo*                io)
{
    acc->state = STATE_INITIAL;
    acc->address = address[0];
    return asyncConsoleConnector_run(acc, io);
}


AsyncStatus
asyncConsoleConnector_run(AsyncConsoleConnector* acc,
                          LoopIo*                io)
{
    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, io);
            if (status == ASYNC_ERROR)
                goto SET_ERROR;

            if (status == ASYNC_COMPLETE) { /* immediate connection */
                _acc_prepareLineReader(acc, io, STATE_READ_BANNER_1);
                continue;
            }
            break;

        case STATE_CONNECTING: /* still trying to connect */
            status = asyncConnector_run(acc->connector, io);
            if (status == ASYNC_ERROR)
                goto SET_ERROR;

            if (status == ASYNC_COMPLETE) {
                _acc_prepareLineReader(acc, io, STATE_READ_BANNER_1);
                continue;
            }
            break;

        case STATE_READ_BANNER_1: /* reading the first banner line */
            status = asyncLineReader_read(acc->lreader, io);
            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, io, STATE_READ_BANNER_2);
                continue;
            }
            break;

        case STATE_READ_BANNER_2: /* reading the second banner line */
            status = asyncLineReader_read(acc->lreader, io);
            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;
}