1 /*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "android/async-console.h"
17 #include <string.h>
18
19 /*
20 * State diagram, ommitting the ERROR state
21 *
22 * INITIAL -->--+
23 * | |
24 * | CONNECTING
25 * | |
26 * |<-----+
27 * v
28 * READ_BANNER_1
29 * |
30 * v
31 * READ_BANNER_2
32 * |
33 * v
34 * COMPLETE
35 */
36
37 enum {
38 STATE_INITIAL,
39 STATE_CONNECTING,
40 STATE_ERROR,
41 STATE_READ_BANNER_1,
42 STATE_READ_BANNER_2,
43 STATE_COMPLETE
44 };
45
46 /* A helper function to prepare the line reader and switch to a new state */
47 static AsyncStatus
_acc_prepareLineReader(AsyncConsoleConnector * acc,int newState)48 _acc_prepareLineReader(AsyncConsoleConnector* acc, int newState)
49 {
50 acc->state = newState;
51 asyncLineReader_init(acc->lreader, acc->lbuff, sizeof(acc->lbuff), acc->io);
52 return ASYNC_NEED_MORE;
53 }
54
55 AsyncStatus
asyncConsoleConnector_connect(AsyncConsoleConnector * acc,const SockAddress * address,LoopIo * io)56 asyncConsoleConnector_connect(AsyncConsoleConnector* acc,
57 const SockAddress* address,
58 LoopIo* io)
59 {
60 acc->state = STATE_INITIAL;
61 acc->address = address[0];
62 acc->io = io;
63 return asyncConsoleConnector_run(acc);
64 }
65
66
67 AsyncStatus
asyncConsoleConnector_run(AsyncConsoleConnector * acc)68 asyncConsoleConnector_run(AsyncConsoleConnector* acc)
69 {
70 AsyncStatus status = ASYNC_NEED_MORE;
71
72 for (;;) {
73 switch (acc->state)
74 {
75 case STATE_ERROR: /* reporting previous error */
76 errno = acc->error;
77 return ASYNC_ERROR;
78
79 case STATE_INITIAL: /* initial connection attempt */
80 acc->state = STATE_CONNECTING;
81 status = asyncConnector_init(acc->connector, &acc->address, acc->io);
82 if (status == ASYNC_ERROR)
83 goto SET_ERROR;
84
85 if (status == ASYNC_COMPLETE) { /* immediate connection */
86 _acc_prepareLineReader(acc, STATE_READ_BANNER_1);
87 continue;
88 }
89 break;
90
91 case STATE_CONNECTING: /* still trying to connect */
92 status = asyncConnector_run(acc->connector);
93 if (status == ASYNC_ERROR)
94 goto SET_ERROR;
95
96 if (status == ASYNC_COMPLETE) {
97 _acc_prepareLineReader(acc, STATE_READ_BANNER_1);
98 continue;
99 }
100 break;
101
102 case STATE_READ_BANNER_1: /* reading the first banner line */
103 status = asyncLineReader_read(acc->lreader);
104 if (status == ASYNC_ERROR)
105 goto SET_ERROR;
106
107 if (status == ASYNC_COMPLETE) {
108 /* Check that first line starts with "Android Console:",
109 * otherwise we're not talking to the right program. */
110 const char* line = asyncLineReader_getLine(acc->lreader);
111 if (line == NULL || memcmp(line, "Android Console:", 16)) {
112 goto BAD_BANNER;
113 }
114 /* ok, fine, prepare for the next banner line then */
115 _acc_prepareLineReader(acc, STATE_READ_BANNER_2);
116 continue;
117 }
118 break;
119
120 case STATE_READ_BANNER_2: /* reading the second banner line */
121 status = asyncLineReader_read(acc->lreader);
122 if (status == ASYNC_ERROR)
123 goto SET_ERROR;
124
125 if (status == ASYNC_COMPLETE) {
126 const char* line = asyncLineReader_getLine(acc->lreader);
127 if (line == NULL) {
128 goto BAD_BANNER;
129 }
130 /* ok, we're done !*/
131 acc->state = STATE_COMPLETE;
132 return ASYNC_COMPLETE;
133 }
134 break;
135
136 case STATE_COMPLETE:
137 status = ASYNC_COMPLETE;
138 }
139 return status;
140 }
141 BAD_BANNER:
142 errno = ENOPROTOOPT;
143 SET_ERROR:
144 acc->state = STATE_ERROR;
145 acc->error = errno;
146 return ASYNC_ERROR;
147 }
148