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