1 /*
2 * Copyright (C) 2008 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
17 #define LOG_TAG "FrameworkListener"
18
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include <log/log.h>
25 #include <sysutils/FrameworkCommand.h>
26 #include <sysutils/FrameworkListener.h>
27 #include <sysutils/SocketClient.h>
28
29 static const int CMD_BUF_SIZE = 1024;
30
31 #define UNUSED __attribute__((unused))
32
FrameworkListener(const char * socketName,bool withSeq)33 FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
34 SocketListener(socketName, true, withSeq) {
35 init(socketName, withSeq);
36 }
37
FrameworkListener(const char * socketName)38 FrameworkListener::FrameworkListener(const char *socketName) :
39 SocketListener(socketName, true, false) {
40 init(socketName, false);
41 }
42
FrameworkListener(int sock)43 FrameworkListener::FrameworkListener(int sock) :
44 SocketListener(sock, true) {
45 init(NULL, false);
46 }
47
init(const char * socketName UNUSED,bool withSeq)48 void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
49 mCommands = new FrameworkCommandCollection();
50 errorRate = 0;
51 mCommandCount = 0;
52 mWithSeq = withSeq;
53 mSkipToNextNullByte = false;
54 }
55
onDataAvailable(SocketClient * c)56 bool FrameworkListener::onDataAvailable(SocketClient *c) {
57 char buffer[CMD_BUF_SIZE];
58 int len;
59
60 len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
61 if (len < 0) {
62 SLOGE("read() failed (%s)", strerror(errno));
63 return false;
64 } else if (!len) {
65 return false;
66 } else if (buffer[len-1] != '\0') {
67 SLOGW("String is not zero-terminated");
68 android_errorWriteLog(0x534e4554, "29831647");
69 c->sendMsg(500, "Command too large for buffer", false);
70 mSkipToNextNullByte = true;
71 return true;
72 }
73
74 int offset = 0;
75 int i;
76
77 for (i = 0; i < len; i++) {
78 if (buffer[i] == '\0') {
79 /* IMPORTANT: dispatchCommand() expects a zero-terminated string */
80 if (mSkipToNextNullByte) {
81 mSkipToNextNullByte = false;
82 } else {
83 dispatchCommand(c, buffer + offset);
84 }
85 offset = i + 1;
86 }
87 }
88
89 mSkipToNextNullByte = false;
90 return true;
91 }
92
registerCmd(FrameworkCommand * cmd)93 void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
94 mCommands->push_back(cmd);
95 }
96
dispatchCommand(SocketClient * cli,char * data)97 void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
98 FrameworkCommandCollection::iterator i;
99 int argc = 0;
100 char *argv[FrameworkListener::CMD_ARGS_MAX];
101 char tmp[CMD_BUF_SIZE];
102 char *p = data;
103 char *q = tmp;
104 char *qlimit = tmp + sizeof(tmp) - 1;
105 bool esc = false;
106 bool quote = false;
107 bool haveCmdNum = !mWithSeq;
108
109 memset(argv, 0, sizeof(argv));
110 memset(tmp, 0, sizeof(tmp));
111 while(*p) {
112 if (*p == '\\') {
113 if (esc) {
114 if (q >= qlimit)
115 goto overflow;
116 *q++ = '\\';
117 esc = false;
118 } else
119 esc = true;
120 p++;
121 continue;
122 } else if (esc) {
123 if (*p == '"') {
124 if (q >= qlimit)
125 goto overflow;
126 *q++ = '"';
127 } else if (*p == '\\') {
128 if (q >= qlimit)
129 goto overflow;
130 *q++ = '\\';
131 } else {
132 cli->sendMsg(500, "Unsupported escape sequence", false);
133 goto out;
134 }
135 p++;
136 esc = false;
137 continue;
138 }
139
140 if (*p == '"') {
141 if (quote)
142 quote = false;
143 else
144 quote = true;
145 p++;
146 continue;
147 }
148
149 if (q >= qlimit)
150 goto overflow;
151 *q = *p++;
152 if (!quote && *q == ' ') {
153 *q = '\0';
154 if (!haveCmdNum) {
155 char *endptr;
156 int cmdNum = (int)strtol(tmp, &endptr, 0);
157 if (endptr == NULL || *endptr != '\0') {
158 cli->sendMsg(500, "Invalid sequence number", false);
159 goto out;
160 }
161 cli->setCmdNum(cmdNum);
162 haveCmdNum = true;
163 } else {
164 if (argc >= CMD_ARGS_MAX)
165 goto overflow;
166 argv[argc++] = strdup(tmp);
167 }
168 memset(tmp, 0, sizeof(tmp));
169 q = tmp;
170 continue;
171 }
172 q++;
173 }
174
175 *q = '\0';
176 if (argc >= CMD_ARGS_MAX)
177 goto overflow;
178 argv[argc++] = strdup(tmp);
179 #if 0
180 for (int k = 0; k < argc; k++) {
181 SLOGD("arg[%d] = '%s'", k, argv[k]);
182 }
183 #endif
184
185 if (quote) {
186 cli->sendMsg(500, "Unclosed quotes error", false);
187 goto out;
188 }
189
190 if (errorRate && (++mCommandCount % errorRate == 0)) {
191 /* ignore this command - let the timeout handler handle it */
192 SLOGE("Faking a timeout");
193 goto out;
194 }
195
196 for (i = mCommands->begin(); i != mCommands->end(); ++i) {
197 FrameworkCommand *c = *i;
198
199 if (!strcmp(argv[0], c->getCommand())) {
200 if (c->runCommand(cli, argc, argv)) {
201 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
202 }
203 goto out;
204 }
205 }
206 cli->sendMsg(500, "Command not recognized", false);
207 out:
208 int j;
209 for (j = 0; j < argc; j++)
210 free(argv[j]);
211 return;
212
213 overflow:
214 cli->sendMsg(500, "Command too long", false);
215 goto out;
216 }
217