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