• 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 
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