Home | History | Annotate | Download | only in src
      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 
     32 FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
     33                             SocketListener(socketName, true, withSeq) {
     34     init(socketName, withSeq);
     35 }
     36 
     37 FrameworkListener::FrameworkListener(const char *socketName) :
     38                             SocketListener(socketName, true, false) {
     39     init(socketName, false);
     40 }
     41 
     42 FrameworkListener::FrameworkListener(int sock) :
     43                             SocketListener(sock, true) {
     44     init(NULL, false);
     45 }
     46 
     47 void FrameworkListener::init(const char *socketName UNUSED, bool withSeq) {
     48     mCommands = new FrameworkCommandCollection();
     49     errorRate = 0;
     50     mCommandCount = 0;
     51     mWithSeq = withSeq;
     52 }
     53 
     54 bool 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 
     81 void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
     82     mCommands->push_back(cmd);
     83 }
     84 
     85 void 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);
    195 out:
    196     int j;
    197     for (j = 0; j < argc; j++)
    198         free(argv[j]);
    199     return;
    200 
    201 overflow:
    202     LOG_EVENT_INT(78001, cli->getUid());
    203     cli->sendMsg(500, "Command too long", false);
    204     goto out;
    205 }
    206