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